深入理解 Java 类加载与垃圾回收机制:从原理到实践
引言
Java 虚拟机(JVM)是整个 Java 技术体系的基石,其中“类加载机制”和“垃圾回收机制”是最核心的两大模块。理解它们,不仅能让我们在日常开发中少踩坑,更能在性能调优时游刃有余。
一、类初始化与类加载机制
1.1 创建对象的过程
在 Java 中通过 new
关键字创建对象,实际上经历了 五个步骤:
-
类加载检查
当执行new
时,JVM 会先检查该类是否已被加载、连接与初始化。若未加载,会触发类加载过程。 -
分配内存空间
JVM 会在堆中为对象分配一段连续空间,空间大小由类定义的字段决定。 -
初始化零值操作
对象内存分配完成后,成员变量会被设置为默认零值(如int=0
、boolean=false
、引用=null)。 -
设置对象头信息
JVM 会在对象头中存储类的元数据、哈希码、GC年龄、锁状态等。 -
执行构造方法
<init>
执行构造器逻辑,完成开发者定义的初始化。
1.2 对象的生命周期
对象的生命周期通常包括三个阶段:
阶段 | 描述 |
---|---|
创建(Creation) | 执行 new,完成初始化 |
使用(In Use) | 程序对对象的访问与操作 |
销毁(Destroy) | 对象无引用后被 GC 回收 |
1.3 类加载器(ClassLoader)
JVM 提供了分层的类加载器体系,用于实现类的隔离与模块化加载。
类加载器 | 加载范围 | 说明 |
---|---|---|
Bootstrap ClassLoader | java.* 核心类库 | JVM 内部实现(C++) |
Extension ClassLoader | jre/lib/ext 目录下的类 | 加载扩展类库 |
Application ClassLoader | 用户类路径(classpath) | 加载项目中的类 |
自定义 ClassLoader | 由开发者继承实现 | 用于热加载、插件化等场景 |
1.4 双亲委派模型(Parent Delegation Model)
双亲委派是类加载体系的核心机制,其原则是:
“向上委派,向下加载”
即:当一个类加载器收到加载请求时,会优先交由父加载器尝试加载;若父加载器无法完成,才由子加载器加载。
✅ 优点总结:
-
保证类的唯一性:避免同一个类被多个类加载器重复加载。
-
保证安全性:防止用户自定义的类覆盖核心类库(如
java.lang.String
)。 -
分层与隔离:不同模块使用不同加载器,逻辑更清晰。
-
简化加载流程:减少重复加载逻辑。
1.5 类加载的完整过程
类加载从“加载”到“卸载”共分 七个阶段:
阶段 | 说明 |
---|---|
加载(Loading) | 读取字节码文件并转换为 Class 对象 |
验证(Verification) | 校验格式、元数据、字节码安全性 |
准备(Preparation) | 分配静态变量内存并赋初值 |
解析(Resolution) | 符号引用 → 直接引用 |
初始化(Initialization) | 执行静态代码块、构造方法 |
使用(Using) | 类被正常使用 |
卸载(Unloading) | 类与其加载器均被 GC 回收 |
二、垃圾回收机制(Garbage Collection)
2.1 垃圾回收简介
在 C/C++ 中,开发者必须手动释放内存,而 Java 借助 自动垃圾回收(GC)机制,让 JVM 自动检测、标记并清除无用对象,极大降低了内存泄漏与悬垂指针风险。
2.2 GC 的触发方式
触发方式 | 说明 |
---|---|
内存不足 | 堆空间耗尽时自动触发 GC |
手动触发 | 调用 System.gc() (仅建议调试时使用) |
JVM 参数调优 | 如 -XX:+UseG1GC 控制收集器行为 |
内部策略 | JVM 根据内存阈值与对象分配速率自动判断 |
2.3 判断对象是否可回收
① 引用计数法(Reference Counting)
-
每个对象维护一个引用计数。
-
引用数为 0 的对象被视为垃圾。
-
缺陷:无法解决循环引用问题。
② 可达性分析法(Reachability Analysis)
-
从 GC Roots 出发遍历对象图。
-
不可达的对象即为垃圾。
GC Roots 包括:
-
栈中引用的对象;
-
静态变量;
-
常量池引用;
-
JNI 引用。
2.4 常见垃圾回收算法
算法 | 原理 | 优点 | 缺点 |
---|---|---|---|
标记-清除(Mark-Sweep) | 标记存活对象后清除未标记 | 实现简单 | 容易内存碎片化 |
标记-整理(Mark-Compact) | 存活对象向一端移动再清除 | 消除碎片 | 效率较低 |
复制算法(Copying) | 将存活对象复制到另一空间 | 无碎片,速度快 | 内存利用率低 |
2.5 常见垃圾回收器(GC Collectors)
收集器 | 区域 | 算法 | 特点 |
---|---|---|---|
Serial | 新生代 | 复制 | 单线程,适合单核 |
ParNew | 新生代 | 复制 | 多线程版 Serial |
Parallel Scavenge | 新生代 | 复制 | 吞吐量优先 |
Serial Old | 老年代 | 标记整理 | 单线程 |
Parallel Old | 老年代 | 标记整理 | 并行 |
CMS | 老年代 | 标记清除 | 低延迟,可能有碎片 |
G1 | 整堆 | 标记整理(分区复制) | 可预测停顿时间,适合大内存场景 |
2.6 Minor GC、Major GC 与 Full GC
类型 | 作用范围 | 触发条件 | 特点 |
---|---|---|---|
Minor GC | 新生代 | Eden 区空间不足 | 频繁,速度快 |
Major GC | 老年代 | 老年代空间不足 | 稍慢 |
Full GC | 整个堆 | 老年代空间不足或方法区溢出 | 停顿时间长 |
2.7 CMS 与 G1 的区别
对比项 | CMS | G1 |
---|---|---|
作用范围 | 老年代 | 整个堆 |
算法类型 | 标记清除 | 标记整理 |
碎片问题 | 有碎片 | 无碎片 |
STW 时间 | 短 | 可预测 |
浮动垃圾 | 存在 | 较少 |
适用场景 | 低延迟系统 | 大内存、高并发系统 |
三、总结与思考
-
类加载机制决定了类的“出生方式”;
-
垃圾回收机制决定了对象的“离开方式”。
深入理解这两者,可以帮助我们:
-
解决类加载冲突与热加载问题;
-
分析内存泄漏与频繁 Full GC;
-
在调优中合理选择 GC 策略。