JVM类加载机制通过加载、验证、准备、解析、初始化五阶段将.class文件转换为Class对象,基于双亲委派模型保障核心类安全,支持自定义类加载器实现热部署、模块化等场景。
一、类加载过程
类加载分为五个阶段,按顺序严格进行(解析阶段可能动态发生):
-
加载(Loading)
- 通过类全限定名获取二进制字节流。
- 将字节流转换为方法区的运行时数据结构。
- 生成
java.lang.Class对象,作为方法区该类的访问入口。
-
验证(Verification)
-
确保字节码符合JVM规范且安全,包括:
- 文件格式验证(魔数、版本等)。
- 元数据验证(语义检查,如继承合法性)。
- 字节码验证(栈帧、类型转换等逻辑)。
- 符号引用验证(解析阶段的预检查)。
-
-
准备(Preparation)
- 为类变量(静态变量)分配内存并设置初始值(零值,如
int=0)。 - 若变量为常量(
final static),直接赋代码中定义的值。
- 为类变量(静态变量)分配内存并设置初始值(零值,如
-
解析(Resolution)
- 将常量池中的符号引用替换为直接引用(内存地址偏移量)。
- 涉及类、字段、方法、接口方法等符号引用解析。
-
初始化(Initialization)
- 执行类构造器
<clinit>()方法,合并类变量赋值和静态代码块。 - JVM保证此方法线程安全,且父类
<clinit>优先执行。
- 执行类构造器
二、类加载器(ClassLoader)
采用分层模型,通过双亲委派机制协作:
-
启动类加载器(Bootstrap ClassLoader)
- C++实现,加载
JAVA_HOME/lib下的核心库(如rt.jar)。 - 唯一没有父加载器的加载器。
- C++实现,加载
-
扩展类加载器(Extension ClassLoader)
- Java实现,加载
JAVA_HOME/lib/ext目录的扩展库。
- Java实现,加载
-
应用程序类加载器(Application ClassLoader)
- 加载用户类路径(ClassPath)的类,默认的类加载器。
-
自定义类加载器
- 继承
ClassLoader,重写findClass()方法。 - 用途:热部署、模块化加载、加密字节码等。
- 继承
三、双亲委派模型(Parent Delegation Model)
-
工作流程
- 类加载请求优先委派父加载器处理。
- 若父加载器无法完成,子加载器才尝试加载。
-
优势
- 避免重复加载,确保核心类安全(如自定义
java.lang.String无效)。 - 类具备层级关系,如Object类由Bootstrap加载,全局唯一。
- 避免重复加载,确保核心类安全(如自定义
-
打破双亲委派
- 场景:如Tomcat需隔离不同Web应用,OSGi实现模块化。
- 方法:重写
loadClass()逻辑(如优先自行加载)。
四、类初始化触发条件
-
主动引用(触发初始化)
new实例化对象、访问/设置静态字段(非常量)、调用静态方法。- 反射调用(
Class.forName())。 - 初始化子类时,若父类未初始化,触发父类初始化。
- JVM启动时的主类(包含
main()方法的类)。
-
被动引用(不触发初始化)
- 通过子类引用父类静态字段,仅父类初始化。
- 定义类的数组(如
ClassA[] arr = new ClassA[10])。 - 引用编译期常量(
final static字段)。
五、常见问题与应用
-
自定义类加载器
- 继承
ClassLoader,重写findClass(),调用defineClass()生成Class对象。
- 继承
-
热部署实现
- 每个模块使用独立类加载器,重新加载修改后的类需新建加载器实例。
-
上下文类加载器
- 用于解决SPI(如JDBC)中父类加载器需访问子类加载器资源的场景。
六、示例代码:自定义类加载器
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] bytes = loadClassData(name); // 从自定义路径读取字节码
return defineClass(name, bytes, 0, bytes.length);
}
private byte[] loadClassData(String className) {
// 实现读取.class文件的逻辑
}
}