Java 的类加载机制是 JVM(Java 虚拟机)的核心组成部分,负责将字节码(.class 文件)加载到内存并转换为可执行状态。以下是关于类加载机制的详细知识点:
一、类加载的基本概念
-
定义
类加载是指将类的字节码数据(.class 文件)读入内存,解析并生成对应的
java.lang.Class
对象,使类能够被 JVM 使用的过程。 -
时机
类加载通常在首次使用类时触发(懒加载),例如:
- 创建类的实例(
new
关键字) - 调用类的静态方法或访问静态变量
- 反射调用(
Class.forName()
) - 初始化子类时(需先加载父类)
- 创建类的实例(
二、类加载的完整流程(生命周期)
类从加载到卸载经历 5 个阶段,其中加载、验证、准备、初始化、卸载 的顺序是固定的,解析阶段可能与初始化交叉进行:
1. 加载(Loading)
- 任务 :通过类的全限定名(如
com.example.User
)获取其字节码数据,并生成Class
对象。 - 来源:字节码可来自本地文件、网络、数据库、动态生成(如 CGLib)等。
- 关键组件:类加载器(ClassLoader)负责此阶段。
2. 验证(Verification)
- 任务:确保字节码符合 JVM 规范,避免恶意或无效字节码危害虚拟机安全。
- 验证内容 :
- 文件格式验证(魔数、版本号等)
- 元数据验证(类继承关系、字段方法合法性)
- 字节码验证(指令逻辑正确性)
- 符号引用验证(常量池引用有效性)
3. 准备(Preparation)
- 任务 :为类的静态变量分配内存并设置初始默认值 (非显式赋值)。
- 例如:
public static int a = 10;
在准备阶段a
的值为0
(int 默认值),显式赋值在初始化阶段执行。
- 例如:
- 注意:仅静态变量(类变量)参与此阶段,实例变量在对象创建时分配内存。
4. 解析(Resolution)
- 任务 :将常量池中的符号引用 (如类名、方法名)转换为直接引用(内存地址)。
- 触发时机:通常在初始化前完成,但某些情况下(如动态绑定)会延迟到运行时解析。
5. 初始化(Initialization)
- 任务 :执行类的初始化逻辑,包括:
- 静态变量的显式赋值
- 静态代码块(
static {}
)的执行
- 顺序规则 :
- 父类初始化优先于子类
- 静态变量和静态代码块按代码出现顺序执行
- 触发条件 (主动使用):
- 创建实例、调用静态方法 / 变量
- 反射(
Class.forName()
) - 启动类(含
main
方法的类) - 初始化子类时触发父类初始化
三、类加载器(ClassLoader)
类加载器负责 "加载" 阶段,通过全限定名获取字节码。JVM 中存在以下几类加载器:
1. 内置类加载器
-
启动类加载器(Bootstrap ClassLoader)
- 由 C++ 实现(非 Java 类),负责加载 JDK 核心类库(如
rt.jar
、resources.jar
)。 - 父加载器为
null
,不继承java.lang.ClassLoader
。
- 由 C++ 实现(非 Java 类),负责加载 JDK 核心类库(如
-
扩展类加载器(Extension ClassLoader)
- 加载 JDK 扩展目录(如
jre/lib/ext
)中的类。 - 父加载器为启动类加载器。
- 加载 JDK 扩展目录(如
-
应用程序类加载器(Application ClassLoader)
- 加载用户类路径(
classpath
)下的类,是默认的类加载器。 - 父加载器为扩展类加载器。
- 加载用户类路径(
2. 自定义类加载器
- 继承
java.lang.ClassLoader
,重写findClass()
方法(推荐)或loadClass()
方法。 - 应用场景:热部署、加密类解密、从非标准来源加载类等。
3. 双亲委派模型
-
定义:类加载时,先委托父加载器加载,父加载器无法加载时才由当前加载器尝试加载。
-
流程 :
自定义加载器 → 应用程序类加载器 → 扩展类加载器 → 启动类加载器
(若所有父加载器均无法加载,则由当前加载器调用
findClass()
加载) -
作用:
- 避免类重复加载(保证同一个类在 JVM 中唯一)。
- 保护核心类库(如
java.lang.String
不会被恶意替换)。
-
破坏双亲委派 :
可通过重写
loadClass()
方法实现(如 Tomcat 的类加载器需隔离不同 Web 应用)。
四、类的卸载
- 当类的
Class
对象不再被引用,且加载该类的类加载器被回收时,类会被卸载。 - 条件:
- 该类的所有实例已被回收。
- 该类的
Class
对象无引用。 - 加载该类的类加载器已被回收。
- 注意:JVM 内置加载器加载的类(如核心类库)通常不会被卸载,因为其加载器始终存在。
五、关键特性与问题
-
类的唯一性
一个类的唯一性由 "全限定名 + 类加载器" 共同决定。不同加载器加载的同全限定名类视为不同类。
-
延迟加载与预加载
- 大多数类采用延迟加载(首次使用时加载)。
- 少数类(如
java.lang.Object
)会被 JVM 预加载。
-
常见异常
ClassNotFoundException
:类加载器无法找到指定类(如类路径错误)。NoClassDefFoundError
:类编译时存在,但运行时未找到(如依赖缺失)。
总结
Java 类加载机制通过 "加载 - 验证 - 准备 - 解析 - 初始化" 的流程,结合双亲委派模型,保证了类的安全加载和 JVM 的稳定性。理解类加载机制对排查类冲突、实现热部署等场景至关重要。