Java类加载机制( 完整生命周期)
Java类加载机制是JVM将类的字节码文件加载到内存,并转化为Class对象的过程。主要包括加载、验证、准备、解析、初始化五个阶段。
一、类加载的生命周期
加载 → 验证 → 准备 → 解析 → 初始化 → 使用 → 卸载

链接(Linking)位于加载(Loading) 和初始化(Initialization) 之间,包含了三个子阶段:验证、准备和解析。核心任务是将加载进来的类的二进制数据,整合到 JVM 的运行时状态中,使其可以被执行。
缓存机制
JVM通过ClassLoader + 类全限定名作为缓存的Key,保证每个类在每个ClassLoader中只完整加载一次,后续使用直接返回缓存的Class对象。类的缓存信息分布在三个区域:元空间、堆、以及JVM内部结构。

类加载的5个阶段(加载、验证、准备、解析、初始化)中,已经加载过的类不会重新执行整个流程。

二、类加载器
层次结构(双亲委派模型)
核心原则:先向上委派,再自己加载

工作流程:
-
收到加载请求,先向上委派给父加载器
-
父加载器继续向上委派,直到顶层的启动类加载器
-
从顶层开始尝试加载,能加载则返回
-
不能加载则向下传递,让子加载器尝试
-
最终都无法加载则抛出异常
各加载器职责
| 类加载器 | 加载路径 | 说明 |
|---|---|---|
| Bootstrap | $JAVA_HOME/jre/lib/rt.jar |
C++实现,JVM内置 |
| Extension | $JAVA_HOME/jre/lib/ext/* |
标准扩展API |
| Application | CLASSPATH |
加载应用类 |
| Custom | 自定义路径 | 用户自定义 |
为什么这样设计?
-
避免重复加载:父加载器加载过的类,子加载器不再重复加载
-
保证核心库安全:用户无法自定义
java.lang.String替换核心类 -
隔离性:不同类加载器加载的类相互隔离
三、类加载时机
主动引用(会触发初始化)
-
new实例化对象
new 实例化对象时,如果该类尚未被加载,会触发该类的完整加载、连接和初始化过程。 -
访问静态变量/方法
访问静态变量/方法是访问类的元数据,而类的元数据在类被加载到内存后才会存在。 -
反射调用(Class.forName())
Class.forName() 默认会触发被加载类的完整初始化(包括静态代码块执行、静态变量赋值)。 -
子类初始化,父类先初始化
当初始化一个类时,如果它的父类还没有被初始化,那么会先递归地初始化父类(一直向上到 Object 类)。 -
启动类的main方法
启动类的main方法会触发启动类(即包含main方法的那个类)本身的加载
被动引用(不会触发初始化)
// 1. 通过子类引用父类静态变量
System.out.println(Child.parentStatic); // 只初始化父类
// 2. 数组定义
Parent[] arr = new Parent[10]; // 不初始化Parent
new一个数组时,只是在创建一个容器对象,并没有真正使用到元素类本身的功能。
// 3. 引用静态常量
System.out.println(Class.FINAL_CONST); // 编译期常量已存入调用类常量池
外部访问编译期常量不需要类加载(值已内联)
内部访问编译期常量也不需要类加载(值也被内联)
编译期常量的值在编译时就“刻”到了所有使用它的代码里(无论内部还是外部),运行时不需要原类。准备阶段赋值是为了支持反射、JNI、序列化等需要访问类元数据的场景,不是给代码内联用的。
四、类加载机制的核心
-
按需加载:用到时才加载
-
全权负责:加载的类所依赖的类也由同一加载器加载
-
缓存机制:加载过的类会缓存
-
双亲委派:保证安全性和唯一性
-
沙箱保护:隔离恶意代码,控制权限
更多推荐




所有评论(0)