JVM垃圾回收算法实现原理深度解析
一、核心算法分类与实现原理
1.1 标记-清除算法(Mark-Sweep)
实现流程:
- 标记阶段:从GC Roots出发,通过深度优先搜索(DFS)遍历对象图,标记所有可达对象
- 清除阶段:遍历整个堆内存,回收未被标记的对象
关键实现细节:
- 使用位图(Mark Bitmap)记录对象存活状态
- 采用三色标记法(白色-未访问,灰色-已访问未处理,黑色-已处理)
- 清除阶段会产生内存碎片
伪代码示例:
void markSweep() {
// 标记阶段
for (Object root : GC_ROOTS) {
mark(root);
}
// 清除阶段
for (Object obj : heap) {
if (!obj.marked) {
free(obj);
}
}
}
应用场景:老年代回收(CMS收集器的基础)
1.2 复制算法(Copying)
内存划分:
- Eden区(80%) + Survivor From/To区(各10%)
实现流程:
- 新生代对象优先分配到Eden区
- Minor GC时,将存活对象复制到Survivor区
- 经过多次GC仍存活的对象晋升到老年代
优化策略:
- Bump-the-Pointer:快速分配内存,仅需记录当前指针位置
- TLAB(Thread-Local Allocation Buffer):线程私有分配缓冲区
伪代码示例:
void copyGC() {
// 复制存活对象
for (Object obj : fromSpace) {
if (isLive(obj)) {
copyToToSpace(obj);
}
}
// 交换空间角色
swap(fromSpace, toSpace);
}
应用场景:新生代回收(Serial/Parallel收集器)
1.3 标记-整理算法(Mark-Compact)
实现流程:
- 标记阶段:与标记-清除相同
- 整理阶段:将存活对象向内存一端移动
- 更新所有引用指针
内存整理策略:
- 滑动整理:对象依次紧凑排列
- 空闲列表:记录可用内存块
伪代码示例:
void markCompact() {
// 标记阶段
markPhase();
// 整理阶段
compactPhase();
// 清除末尾空间
sweepTail();
}
应用场景:老年代回收(Serial Old/G1收集器)
1.4 分代收集算法(Generational)
代际划分:
| 代际 | 存活时间 | 回收频率 | 算法组合 |
|---|---|---|---|
| 新生代 | 短(ms) | 高频 | 复制算法 |
| 老年代 | 长(h) | 低频 | 标记-清除/整理 |
| 永久代 | 永久 | 极低 | 无(JDK8后移除) |
分代策略优势:
- 降低老年代回收频率
- 提高新生代回收效率
二、主流垃圾回收器实现
2.1 CMS(Concurrent Mark-Sweep)
工作流程:
- 初始标记(STW):标记GC Roots直接关联对象
- 并发标记:与应用线程并行标记
- 重新标记(STW):修正并发标记期间的变动
- 并发清除:与应用线程并行回收
核心优化:
- 使用写屏障(Write Barrier)记录对象引用变化
- 采用卡表(Card Table)管理跨代引用
缺点:
- 无法处理浮动垃圾
- 内存碎片问题突出
2.2 G1(Garbage-First)
内存结构:
- 将堆划分为2048个大小相等的Region
- 每个Region标记为E(Eden)、S(Survivor)、O(Old)、H(Humongous)
回收策略:
- 年轻代回收:复制算法处理Eden+S区
- 混合回收:并行处理年轻代+部分老年代
- Full GC:单线程标记-整理
核心优势:
- 可预测停顿时间(-XX:MaxGCPauseMillis)
- 支持大堆内存(TB级别)
2.3 ZGC(Z Garbage Collector)
实现创新:
- 染色指针(Colored Pointers):利用对象头64位中的部分位记录GC信息
- 读屏障(Load Barrier):实时识别对象状态
- 并发整理:全阶段并发执行
性能指标:
- 停顿时间<10ms
- 支持TB级堆内存
三、关键实现技术
3.1 可达性分析
GC Roots类型:
- 虚拟机栈中的局部变量
- 方法区的静态属性
- JNI全局引用
- 活跃线程对象
三色标记法:
| 颜色 | 状态说明 |
|---|---|
| 白色 | 未被访问的对象 |
| 灰色 | 已访问但未处理引用的对象 |
| 黑色 | 已访问且处理完引用的对象 |
3.2 内存分配优化
TLAB(Thread-Local Allocation Buffer):
- 每个线程私有的内存分配缓冲区
- 减少锁竞争,提升分配效率
指针碰撞(Bump-the-Pointer):
- 通过记录当前分配指针快速分配内存
- 适用于Eden区的连续内存分配
3.3 并发控制机制
写屏障(Write Barrier):
void oopFieldStore(oop* field, oop newOop) {
// 记录旧引用
recordOldReference(field);
// 更新指针
*field = newOop;
}
卡表(Card Table):
- 将老年代划分为512B的卡页
- 跨代引用时标记对应卡页为脏
四、性能调优实践
4.1 参数配置示例
# G1收集器配置
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45
# ZGC配置
-XX:+UseZGC
-XX:ZAllocationSpikeTolerance=2.0
4.2 GC日志分析
关键指标:
GC pauses:停顿时间Throughput:吞吐量(应用运行时间占比)Humongous Allocation:大对象分配频率
分析工具:
GCViewer:可视化分析GC日志Eclipse MAT:堆转储分析
4.3 常见问题排查
内存泄漏特征:
- Full GC频繁但堆内存无明显下降
- 老年代对象存活率持续增长
诊断步骤:
- 启用
-XX:+HeapDumpOnOutOfMemoryError - 使用
jmap生成堆转储 - 通过MAT分析对象引用链
五、演进趋势
5.1 低延迟方向
- Shenandoah:完全并发的垃圾回收器
- Epsilon:无操作的"空"收集器(用于性能测试)
5.2 大内存支持
- Mmtk(Meta Memory Toolkit):为JVM设计的内存管理框架
- Project Leyden:静态镜像+运行时优化的混合方案
5.3 智能化调优
- 自适应算法:根据应用负载动态调整参数
- AI预测:基于历史数据预测GC行为
通过合理选择垃圾回收算法和调优参数,可将GC停顿时间降低90%以上。建议生产环境根据应用特性(如延迟敏感型选择ZGC,吞吐量优先选择Parallel GC)进行组合优化。