三色标记算法通过白、灰、黑三色标识对象可达性,解决并发标记中因引用修改导致的漏标问题。采用增量更新(CMS)或原始快照(G1)策略,结合写屏障维护标记准确性,确保现代回收器(如G1、ZGC)高效管理内存并减少停顿。
一、三色标记算法的核心思想
三色标记是一种用于 追踪对象可达性 的算法,通过颜色标记对象状态,解决并发标记过程中因应用线程修改引用导致的漏标或错标问题。其核心是将对象分为三种颜色:
| 颜色 | 含义 |
|---|---|
| 白色 | 对象未被垃圾回收器访问过,可能不可达(最终会被回收)。 |
| 灰色 | 对象已被垃圾回收器访问,但其引用的子对象未被扫描(处于待处理状态)。 |
| 黑色 | 对象及其所有子对象均被扫描完成,确认存活,不会被回收。 |
二、三色标记流程
以 可达性分析 为例,三色标记的流程如下:
-
初始标记(STW) :
- 标记所有 GC Roots 直接引用 的对象为灰色,其余对象为白色。
-
并发标记:
-
从灰色对象出发,递归扫描其引用的子对象:
- 将当前灰色对象标记为黑色。
- 将其子对象标记为灰色(若原本是白色)。
-
重复上述步骤,直到所有灰色对象变为黑色。
-
-
最终标记(STW) :
- 处理并发标记期间应用线程修改的引用关系,修正标记结果。
-
清理阶段:
- 回收所有白色对象。
三、并发标记的挑战
在 并发标记阶段,应用线程可能修改对象引用关系,导致以下问题:
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
- 结合 染色指针 和读屏障,实现全堆并发标记。
七、总结
-
三色标记的核心:
- 通过颜色状态跟踪对象可达性,支持并发标记。
- 白色(未访问)、灰色(待处理)、黑色(已处理)。
-
并发标记的问题:
- 漏标(存活对象被回收)和错标(垃圾对象被保留)。
-
解决方案:
- 增量更新:关注引用新增,黑色对象降级为灰色。
- 原始快照(SATB) :关注引用删除,保留旧引用对象。
-
实际应用:
- CMS、G1、ZGC 等回收器通过写屏障实现不同策略,平衡吞吐量和延迟。
理解三色标记算法是掌握现代垃圾回收器(如 G1、ZGC)并发标记机制的基础,有助于优化 JVM 参数和排查 GC 问题。