JVM三色标记垃圾回收算法详解

三色标记算法是现代JVM垃圾回收器(如G1、Shenandoah、ZGC等)中使用的核心算法之一,用于高效地标记存活对象。下面我将详细解释这一算法的工作原理。

基本概念

三色标记算法将对象按照颜色分为三类:

  1. 白色(White):初始状态,表示对象未被访问过(即未被标记),可能是垃圾
  2. 灰色(Grey):表示对象已被标记,但其引用的对象还未被检查
  3. 黑色(Black):表示对象及其所有引用的对象都已被标记,是确定存活的对象

算法工作流程

  1. 初始阶段
  • 所有对象初始为白色
  • 从GC Roots(栈、寄存器、静态变量等)出发,将直接可达的对象标记为灰色
  1. 标记阶段
  • 从灰色集合中取出一个对象:
  • 将其引用的所有白色对象标记为灰色
  • 将该对象本身标记为黑色
  • 重复上述过程,直到灰色集合为空
  1. 回收阶段
  • 所有黑色对象为存活对象
  • 所有白色对象为垃圾对象,可被回收

算法伪代码表示

// 初始化
for (obj in roots) {obj.color = GREY
}// 标记阶段
while (!greySet.isEmpty()) {obj = greySet.remove()for (ref in obj.references()) {if (ref.color == WHITE) {ref.color = GREYgreySet.add(ref)}}obj.color = BLACK
}// 回收阶段
for (obj in heap) {if (obj.color == WHITE) {free(obj)}
}

并发标记与三色不变式

当垃圾回收器需要与用户线程并发执行时,可能出现对象引用关系变化的问题。为确保正确性,必须维护以下两个不变式之一:

  1. 强三色不变式:黑色对象不能直接引用白色对象
  • 解决方案:增量更新(Incremental Update)
  • 当黑色对象引用白色对象时,将黑色对象重新标记为灰色
  1. 弱三色不变式:黑色对象可以引用白色对象,但必须存在灰色对象到该白色对象的可达路径
  • 解决方案:原始快照(Snapshot At The Beginning, SATB)
  • 记录引用关系变化前的状态,确保不遗漏标记

实际应用

  1. CMS收集器:使用写屏障实现增量更新
  2. G1收集器:使用原始快照(SATB)方式
  3. Shenandoah/ZGC:使用更复杂的读/写屏障实现并发标记

优点

  1. 可以高效地并行或并发执行
  2. 通过颜色标记可以清晰地表示对象状态
  3. 适合现代大内存应用的垃圾回收需求

局限性

  1. 需要额外的空间存储颜色信息(通常利用对象头中的标记位)
  2. 并发标记时需处理对象图变化带来的复杂性
  3. 写屏障可能带来一定的运行时开销

三色标记算法是现代垃圾回收技术的基石,使得JVM能够在几乎不影响应用线程的情况下完成垃圾回收工作。