类加载过程

类被加载入 JVM 后,整个生命周期分为七个阶段:加载、验证、准备、解析、初始化、使用和卸载(验证、准备、解析这三个阶段统称为连接)
除去使用和卸载,剩余 5 个阶段顺序发生,就是 Java 的类加载过程(但在动态绑定的情况下,解析阶段发生在初始化阶段之后)
Loading 加载
将字节码转化为二进制字节流加载到内存中,并生成一个代表该类的 java.lang.Class 对象
Verification 验证
该阶段会对二进制字节流进行校验,只有符合 JVM 字节码规范的才能被 JVM 正确执行
Preparation 准备
该阶段对类变量(也称为静态变量,static 关键字修饰的)分配内存并初始化对应数据类型的默认初始值,如 0、0L、null、false 等
public String str1 = "111";
public static String str2 = "222";
public static final String str3 = "333";
str2会被作为类变量分配内存,且初始值为null而不是222,真正的赋值会在 Initialization 初始化阶段
str3会被作为常量变量加载入类的常量池,直接赋值333
Resolution 解析
该阶段将常量池中的符号引用转化为直接引用,将“编译时”和“运行时”解耦
符号引用:
以一组符号(无歧义的定位到目标的字面量)来描述所引用的目标
定义:包含了类、字段、方法、接口等多种符号的全限定名
特点:在编译时生成,存储在编译后的字节码文件的常量池中
直接引用:
通过对符号引用进行解析,找到引用的实际内存地址
定义:直接指向目标的指针、相对偏移量或者能间接定位到目标的句柄
特点:在运行时生成,依赖于具体的内存布局
效率:由于直接指向了内存地址或者偏移量,效率更高
若不涉及动态加载,则进行一次解析后的解析结果可以一直使用
但使用了动态加载,解析结果会根据不同情况发生变化。所以动态加载时,解析阶段在初始化后执行,现解析现使用
Initialization 初始化
在准备阶段,类变量已经被赋过默认初始值,而在初始化阶段,类变量将被赋值为代码期望赋的值
类加载器
概念
类加载器是 JVM 中用于动态加载类文件的组件。它将 .class 文件中的字节码加载到内存中,并将其转换为 Class 对象,以供 JVM 执行
对于任意一个类,都需要由它的类加载器和这个类本身一同确定其在 JVM 中的唯一性
作用
动态加载类:在运行时根据需要加载类,而不是在编译时加载所有类
隔离不同的类命名空间:通过不同的类加载器,可以隔离同名类,使得它们不会相互冲突
双亲委派模型
概念
类加载器在尝试加载一个类时,首先会委派给其父加载器去尝试加载这个类,只有在父加载器无法加载该类时,子加载器才会尝试自己去加载
特点
1)递归委派:这个过程会递归向上进行,从启动类加载器开始,再到扩展类加载器,最后到系统类加载器
2)优先父加载器:如果父加载器可以加载这个类,那么就优先使用父加载器的结果
作用
1)安全性:避免重复加载类。例如 java.lang.Object 类只能由根类加载器加载,防止恶意代码加载不受信任的类来替代系统核心类
2)一致性:保证同一个类在 JVM 中只会被加载一次,确保在整个应用中使用的是同一个类对象
