并发标记通过三色标记法、写屏障和重新标记保障准确性:三色标记法要求无“黑→白”引用,写屏障拦截引用变更(增量更新或satb),重新标记阶段stw修正漏标,zgc/shenandoah则用读屏障或brooks pointer实现近实时响应。

并发标记阶段允许应用线程与 GC 线程同时运行,但堆中对象引用关系可能随时变化,这会导致漏标(本该标记的存活对象没被标记)或错标(已死亡对象被误标)。JVM 通过一套协同机制来保障标记准确性,核心是三色标记法 + 写屏障(Write Barrier)+ 重新标记(Remark)。
三色标记法:给对象打状态标签
标记过程中,每个对象处于以下三种颜色之一:
- 白色:尚未访问,初始状态;若标记结束仍为白色,即被判定为垃圾
- 灰色:已被访问,但其引用的其他对象还未扫描(处于待处理队列中)
- 黑色:已被访问,且其所有直接引用的对象都已扫描完毕
正确性要求是:不能出现“黑色 → 白色”的引用。一旦发生,就意味一个存活对象(白)被黑色对象引用,却未被扫描到,造成漏标。
写屏障拦截引用变更
当应用线程在并发标记期间修改引用(如 a.field = b),JVM 会在赋值操作前后插入一段轻量级检测逻辑——写屏障。常见处理策略有:
- 增量更新(Incremental Update):当发现黑色对象新增了对白色对象的引用,立即将该白色对象重新标记为灰色(加回扫描队列)。CMS 收集器采用此策略
- 原始快照(Snapshot At The Beginning, SATB):在引用被修改前,先记录下旧引用指向的对象(即使它当时是白色),确保这些“原本可达”的对象不会被误回收。G1、ZGC、Shenandoah 均使用 SATB
写屏障不阻塞应用线程,但会带来微小开销;它是并发标记能安全运行的技术基础。
重新标记(Remark):兜底修正
尽管有写屏障,仍存在极短窗口(如 GC 线程刚扫描完某个对象,应用线程立刻修改其字段)导致状态不一致。因此,在并发标记结束后,JVM 会触发一次短暂的 STW 阶段——重新标记:
- 扫描所有被写屏障记录的“脏卡”(Card Table 中标记为 dirty 的内存页)
- 重新处理 GC Roots 的直接引用(因并发期间栈可能变化)
- 完成最终的灰色→黑色推进,确保无漏标
这个阶段停顿时间远小于完整标记,但仍是 CMS 和 G1 中较明显的 STW 环节。
现代收集器的演进方向
ZGC 和 Shenandoah 进一步弱化 Remark 的压力:
- ZGC 使用读屏障(Load Barrier),在对象被读取时动态检查并转发(color-aware load barrier),实现几乎全部并发标记与并发移动
- Shenandoah 在标记和压缩阶段均使用 SATB + Brooks Pointer,把大部分修正逻辑摊平到运行时
它们的目标是将任意阶段的停顿控制在 10ms 以内,关键就在于把引用变化的响应从“集中修正”转向“实时感知”。











