三色标记算法

174 阅读4分钟

三色标记算法通过白、灰、黑三色标识对象可达性,解决并发标记中因引用修改导致的漏标问题。采用增量更新(CMS)或原始快照(G1)策略,结合写屏障维护标记准确性,确保现代回收器(如G1、ZGC)高效管理内存并减少停顿。


一、三色标记算法的核心思想

三色标记是一种用于 追踪对象可达性 的算法,通过颜色标记对象状态,解决并发标记过程中因应用线程修改引用导致的漏标或错标问题。其核心是将对象分为三种颜色:

颜色含义
白色对象未被垃圾回收器访问过,可能不可达(最终会被回收)。
灰色对象已被垃圾回收器访问,但其引用的子对象未被扫描(处于待处理状态)。
黑色对象及其所有子对象均被扫描完成,确认存活,不会被回收。

二、三色标记流程

可达性分析 为例,三色标记的流程如下:

  1. 初始标记(STW)

    • 标记所有 GC Roots 直接引用 的对象为灰色,其余对象为白色。
  2. 并发标记

    • 从灰色对象出发,递归扫描其引用的子对象:

      • 将当前灰色对象标记为黑色。
      • 将其子对象标记为灰色(若原本是白色)。
    • 重复上述步骤,直到所有灰色对象变为黑色。

  3. 最终标记(STW)

    • 处理并发标记期间应用线程修改的引用关系,修正标记结果。
  4. 清理阶段

    • 回收所有白色对象。

三、并发标记的挑战

并发标记阶段,应用线程可能修改对象引用关系,导致以下问题:

1. 漏标(对象被错误回收)

  • 场景:黑色对象 A 断开对白色对象 B 的引用,同时灰色对象 C 引用白色对象 B。

  • 结果:B 被误判为不可达(白色),导致存活对象被回收。

  • 条件:需同时满足以下两点(称为 “对象消失” 的必要条件):

    • 赋值器插入了一条或多条从黑色对象到白色对象的新引用
    • 赋值器删除了全部从灰色对象到该白色对象的直接或间接引用

2. 错标(对象被错误保留)

  • 场景:黑色对象 A 引用白色对象 B,但 B 实际不可达。
  • 结果:B 被误判为可达(保留),导致内存泄漏(较少见)。

四、解决方案:增量更新与原始快照

JVM 通过 写屏障(Write Barrier) 技术拦截对象引用变更,结合两种策略解决并发标记问题:

1. 增量更新(Incremental Update)

  • 核心思想:若黑色对象新增对白色对象的引用,将黑色对象降级为灰色,重新扫描。

  • 实现

    • 写屏障记录所有新增的引用关系(如 A→B)。
    • 在最终标记阶段,重新扫描这些黑色对象。
  • 代表应用:CMS 回收器的并发标记阶段。

2. 原始快照(Snapshot At The Beginning, SATB)

  • 核心思想:基于标记开始时的对象图快照,认为所有当时存活的对象最终都会被标记。

  • 实现

    • 写屏障记录所有被删除的引用关系(如 C→B 被删除)。
    • 在最终标记阶段,将这些被删除引用的对象重新标记为灰色,确保其存活。
  • 代表应用:G1、Shenandoah 回收器。


五、写屏障(Write Barrier)的实现

写屏障是 内存访问的钩子函数,在对象引用被修改时触发,记录变更信息。

示例代码

// 假设当前有黑色对象 parent 和白色对象 child
parent.field = child;  // 触发写屏障

1. 增量更新的写屏障伪代码

void write_barrier(Obj* parent, Obj* child) {
    if (is_black(parent) && is_white(child)) {
        mark_gray(parent);  // 将 parent 标记为灰色
    }
    *field = child;  // 实际赋值操作
}

2. 原始快照的写屏障伪代码

void write_barrier(Obj* old_child) {
    if (old_child != NULL && is_white(old_child)) {
        mark_gray(old_child);  // 记录被删除的旧引用对象
    }
    *field = new_child;  // 实际赋值操作
}

六、三色标记与垃圾回收器的应用

1. CMS(Concurrent Mark-Sweep)

  • 使用 增量更新 解决漏标问题。
  • 缺点:并发标记期间可能产生浮动垃圾(错标)。

2. G1(Garbage-First)

  • 使用 SATB 确保标记准确性。
  • 优点:减少漏标,适合大堆内存。

3. ZGC/Shenandoah

  • 结合 染色指针 和读屏障,实现全堆并发标记。

七、总结

  1. 三色标记的核心

    • 通过颜色状态跟踪对象可达性,支持并发标记。
    • 白色(未访问)、灰色(待处理)、黑色(已处理)。
  2. 并发标记的问题

    • 漏标(存活对象被回收)和错标(垃圾对象被保留)。
  3. 解决方案

    • 增量更新:关注引用新增,黑色对象降级为灰色。
    • 原始快照(SATB) :关注引用删除,保留旧引用对象。
  4. 实际应用

    • CMS、G1、ZGC 等回收器通过写屏障实现不同策略,平衡吞吐量和延迟。

理解三色标记算法是掌握现代垃圾回收器(如 G1、ZGC)并发标记机制的基础,有助于优化 JVM 参数和排查 GC 问题。