最近在准备面试,正把平时积累的笔记、项目中遇到的问题与解决方案、对核心原理的理解,以及高频业务场景的应对策略系统梳理一遍,既能加深记忆,也能让知识体系更扎实,供大家参考,欢迎讨论。
一、类加载生命周期
JVM 中类的生命周期分为七个阶段:
加载 → 验证 → 准备 → 解析 → 初始化
→ 使用 → 卸载
其中前五个阶段通常统称为 类加载过程 。
举一反三 :类加载的流程类比到工作中处理渠道方计费数据的流程
类加载阶段 | 数据处理对应步骤 | 说明 |
---|---|---|
加载 Loading | 下载数据到本地 / 读取文件到内存 | 把外部资源引入系统,类似 JVM 从硬盘读取字节码 |
验证 Verification | 校验数据合法性 | 检查格式、字段类型、业务规则是否正确,保证安全可靠 |
准备 Preparation | 设计变量 / 初始化内存空间 | 给处理变量分配存储空间并赋初始值,为后续解析做好准备 |
解析 Resolution | 按行解析数据 / 将原始数据映射到结构化对象 | 把原始数据转化为程序可用的内部表示 |
初始化 Initialization | 给对象赋值 / 封装批量数据 | 按业务规则给字段赋值,组装成可操作的批量数据 |
使用 Using | 后续业务处理 / 保存数据库 | 数据真正被业务使用或写入存储 |
卸载 Unloading | 释放内存 / 清理对象 | 内存释放,避免占用,类似 JVM 卸载类和 ClassLoader |
二、各阶段详解
1. 加载(Loading)
- 通过类的 全限定名 获取字节码。
- 在硬盘上查找
.class
(.java编译的结果) 文件并通过 I/O 读取。 JVM 会把字节码文件从硬盘读取进内存
,并在方法区(JDK 8 之后叫元空间
(Metaspace),使用本地内存)中建立对应的运行时数据结构,包括类的元信息(类名、父类、接口)、字段表、方法表和运行时常量池等,这样类才具备在使用期间被实例化、方法调用和数据访问的基础
。- 在堆中生成
java.lang.Class
对象作为访问入口。 - 触发条件 :调用
main()
方法、new
对象、访问类的静态成员等。
2. 验证(Verification)
-
目的 :
确保字节码文件合法、安全
,如果发现问题(比如非法指令、越界访问、破坏 JVM 约束),就会拒绝加载,从而避免危害虚拟机的稳定性和安全性。正常我们写的程序都不会被拒绝的。 -
检查内容:
- 文件格式验证。
- 元数据验证。
- 字节码验证。
- 符号引用验证。
3. 准备(Preparation)
- 为类的静态变量分配内存,并设零值。
- 注意:并不是赋代码中的值,而是赋默认值。
java
public static int a = 10;
// 准备阶段:a = 0
// 初始化阶段完成赋值:a = 10
4. 解析(Resolution)
- 将常量池中的 符号引用 替换为 直接引用。
- 属于 静态链接过程。
- 对应的 动态链接:在运行时(方法调用时)才进行。
5. 初始化(Initialization)
-
执行类的初始化逻辑:
- 按照源码顺序,为静态变量赋值。
- 执行静态代码块。
java
public class Demo {
static int a = 10;
static { a = 20; }
}
// 初始化后 a = 20
6. 使用(Using)
-
类进入正常使用阶段:
- 调用类静态方法(如
main()
)。 - new 对象。
- 调用对象实例方法。
- 调用类静态方法(如
7. 卸载(Unloading)
- 当 ClassLoader 和类实例都不再被引用 时,JVM 会回收它们,触发类卸载。类卸载可以释放方法区(或元空间)占用的内存,支持动态部署和更新。
三、整体回顾
- 加载 :读取
.class
,生成Class
对象。 - 验证:确保安全合法。
- 准备:静态变量赋零值。
- 解析:符号引用 → 直接引用。
- 初始化:赋初始值,执行静态块。
- 使用:new 对象,方法调用。
- 卸载:Class 对象回收。