并发标记阶段通过三色标记法(白/灰/黑)在用户线程运行时识别存活对象,依赖写屏障拦截引用变更以防止漏标,再标记阶段stw修正黑→白新增引用或灰→白删除引用导致的漏标问题。

并发标记阶段是 CMS、G1 等现代垃圾回收器实现低停顿的关键环节,它的核心目标是在用户线程(Mutator)持续运行的同时,准确识别出所有存活对象。它不追求绝对原子性,而依赖“三色标记法”这一逻辑框架,并通过写屏障(Write Barrier)等机制来应对并发修改带来的漏标风险。
三色标记作为并发标记的逻辑骨架
整个堆在逻辑上被划分为白、灰、黑三类对象:
- 白色对象:尚未被 GC 访问过,初始全部为白;标记结束时仍为白的即判定为垃圾。
- 灰色对象:已被 GC 访问,但其引用的子对象尚未全部扫描完毕;它是当前扫描推进的“工作队列”。
- 黑色对象:已完全扫描完毕,且其直接引用的对象均已涂灰或变黑;GC 不再重新访问黑色对象,且它不能直接指向白色对象(否则会破坏标记完整性)。
并发标记的典型执行流程
以 CMS 或 G1 的并发标记为例,该阶段通常紧接在初始标记(STW)之后启动:
- 初始标记后,所有从 GC Roots 直接可达的对象被涂灰并入队。
- GC 线程从灰色集合中取出一个对象,遍历其所有引用字段,将每个未访问过的引用对象由白转灰(若已是灰或黑则跳过),并将该灰对象自身涂黑。
- 此过程与用户线程并发执行——用户线程可能修改对象引用(如赋值、置 null),导致对象图动态变化。
- 当灰色集合为空时,本轮并发标记逻辑完成,但此时结果可能不精确(存在漏标),需后续再标记阶段修正。
为什么需要再标记?——并发导致的漏标问题
用户线程在并发标记期间的两类操作可能破坏三色不变式:
- 赋值器插入新引用:A→B(A 已黑,B 原为白),若 B 尚未被扫描,就形成黑→白边,B 可能被漏标。
- 赋值器删除引用:A→B 被断开,而 A 已黑、B 仍为白,若 B 无其他路径可达,则成为“悬挂”的白色存活对象,也会漏标。
为拦截这类变更,JVM 在写操作(如 putfield)前后插入写屏障,将被修改的引用或相关对象记录到“卡表(Card Table)”或“增量更新/原始快照(SATB)缓冲区”,供再标记阶段集中扫描。
再标记阶段的作用与特点
再标记(Remark)是一个短暂的 Stop-the-World 阶段,主要做两件事:
- 扫描写屏障记录的“脏卡”或 SATB 缓冲区,对其中涉及的对象及其子图进行补充标记。
- 重新扫描 GC Roots(包括栈帧、JNI 引用、全局变量等),确保所有新出现的根引用都被覆盖。
由于大部分对象已在并发标记中处理完毕,再标记只需聚焦增量变化,因此 STW 时间远短于初始标记,是控制整体停顿的关键节点。











