类加载器
1. 类加载器简介
类加载器(ClassLoader)是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术。ClassLoader负责通过各种方式将Class信息的二进制数据流读入JVM内部,转换为一个与目标类对应的java.lang.Class对象实例。然后交给Java虚拟机进行链接、初始化等操作。
因此ClassLoader在整个装载阶段,只能影响到类的加载,而无法通过ClassLoader去改变类的链接和初始化行为。至于它是否可以运行,则由Execution Engine决定。
2. 类加载器的使用场景
2.1 企业级应用
SPI机制,类的热部署,Tomcat的类隔离
2.2 解决线上问题
使用Arthas不停机解决线上故障
3. 类加载器分类
类加载器分为两类,一类是Java代码中实现的,一类是Java虚拟机底层源码实现的。
3.1 JDK8以及之前
我们重点关注扩展类加载器和应用程序类加载器:
在arthas中使用
classloader
命令查看类加载器加载统计信息: 在arthas中使用
classloader -l -c
命令查看类加载器加载统计信息:
3.2 JDK9以及之后
由于JDK9引入了module的概念,类加载器在设计上发生了很多变化。比如以前不同类加载器加载目录不同,现在改成从jmod文件中加载。 启动类加载器使用从C++变成使用Java编写,位于jdk.internal.loader.ClassLoaders类中。
新出现BootClassLoader继承自BuiltinClassLoader实现从模块中找到要加载的字节码资源文件。
启动类加载器依然无法通过java代码获取到,返回的仍然是null,保持了统一(同时考虑安全性,防止用户直接操作它)。
扩展类加载器被替换成了平台类加载器(Platform Class Loader)。平台类加载器遵循模块化方式加载字节码文件,所以继承关系从URLClassLoader变成了 BuiltinClassLoader,BuiltinClassLoader实现了从模块中加载字节码文件。平台类加载器的存在更多的是 为了与老版本的设计方案兼容,自身没有特殊的逻辑。
4. 启动类加载器
启动类加载器(Bootstrap ClassLoader)是由Hotspot虚拟机提供的、使用C++编写的类加载器,默认加载Java安装目录/jre/lib下的类文件,比如rt.jartools.jar,resources.jar等。 使用
-XX:+TraceClassLoading
参数可以打印JVM启动类加载情况: 可以通过启动类加载器去加载用户jar包,不推荐直接放入jre/lib下,因为虽然放进去了但仍有可能因为文件名不匹配不能正常被加载的情况,推荐的办法是使用
-Xbootclasspath/a:jar包全路径
参数。
5. 扩展类加载器
Java语言编写,由sun.misc.Launcher$ExtClassLoader
实现。源码位于sun.misc.Launcher
中,是一个静态内部类。继承自URLClassLoader。
从java.ext.dirs
系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库。同样不推荐直接放入jre/ext/lib下,推荐使用命令参数:-Djava.ext.dirs=jar包目录
但是会覆盖jre/lib/ext目录,可以用分隔符;
(window环境)或者:
(macos/linux环境)加上jdk的ext目录。
6. 应用程序类加载器
Java语言编写,由sun.misc.Launcher$AppClassLoader
实现。源码位于sun.misc.Launcher
中,也是一个静态内部类。
它负责加载环境变量classpath或系统属性java.class.path
指定路径下的类库。