JVM垃圾回收算法实现原理深度解析

一、核心算法分类与实现原理

1.1 标记-清除算法(Mark-Sweep)

实现流程

  1. 标记阶段:从GC Roots出发,通过深度优先搜索(DFS)遍历对象图,标记所有可达对象
  2. 清除阶段:遍历整个堆内存,回收未被标记的对象

关键实现细节

  • 使用位图(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%)

实现流程

  1. 新生代对象优先分配到Eden区
  2. Minor GC时,将存活对象复制到Survivor区
  3. 经过多次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)

实现流程

  1. 标记阶段:与标记-清除相同
  2. 整理阶段:将存活对象向内存一端移动
  3. 更新所有引用指针

内存整理策略

  • 滑动整理:对象依次紧凑排列
  • 空闲列表:记录可用内存块

伪代码示例

void markCompact() {
    // 标记阶段
    markPhase();
    // 整理阶段
    compactPhase();
    // 清除末尾空间
    sweepTail();
}

应用场景:老年代回收(Serial Old/G1收集器)

1.4 分代收集算法(Generational)

代际划分

代际存活时间回收频率算法组合
新生代短(ms)高频复制算法
老年代长(h)低频标记-清除/整理
永久代永久极低无(JDK8后移除)

分代策略优势

  • 降低老年代回收频率
  • 提高新生代回收效率

二、主流垃圾回收器实现

2.1 CMS(Concurrent Mark-Sweep)

工作流程

  1. 初始标记(STW):标记GC Roots直接关联对象
  2. 并发标记:与应用线程并行标记
  3. 重新标记(STW):修正并发标记期间的变动
  4. 并发清除:与应用线程并行回收

核心优化

  • 使用写屏障(Write Barrier)记录对象引用变化
  • 采用卡表(Card Table)管理跨代引用

缺点

  • 无法处理浮动垃圾
  • 内存碎片问题突出

2.2 G1(Garbage-First)

内存结构

  • 将堆划分为2048个大小相等的Region
  • 每个Region标记为E(Eden)、S(Survivor)、O(Old)、H(Humongous)

回收策略

  1. 年轻代回收:复制算法处理Eden+S区
  2. 混合回收:并行处理年轻代+部分老年代
  3. 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频繁但堆内存无明显下降
  • 老年代对象存活率持续增长

诊断步骤

  1. 启用-XX:+HeapDumpOnOutOfMemoryError
  2. 使用jmap生成堆转储
  3. 通过MAT分析对象引用链

五、演进趋势

5.1 低延迟方向

  • Shenandoah:完全并发的垃圾回收器
  • Epsilon:无操作的"空"收集器(用于性能测试)

5.2 大内存支持

  • Mmtk(Meta Memory Toolkit):为JVM设计的内存管理框架
  • Project Leyden:静态镜像+运行时优化的混合方案

5.3 智能化调优

  • 自适应算法:根据应用负载动态调整参数
  • AI预测:基于历史数据预测GC行为

通过合理选择垃圾回收算法和调优参数,可将GC停顿时间降低90%以上。建议生产环境根据应用特性(如延迟敏感型选择ZGC,吞吐量优先选择Parallel GC)进行组合优化。