三色标记算法在并发标记阶段的核心目标是让gc线程与用户线程并发运行时仍准确标记存活对象,避免误删;其通过白色(未访问)、灰色(已访问但引用未扫完)、黑色(已访问且引用全扫完)状态流转实现,关键约束是黑色对象不可直接引用白色对象,漏标需同时满足“黑→白新增引用”和“灰→白引用断开”两个条件,cms用增量更新(黑→白时将黑对象重置为灰)解决,g1用satb(记录引用删除并事后重扫)解决,浮动垃圾因只影响效率、不破坏正确性而可容忍。

三色标记算法在并发标记阶段的核心目标是:让 GC 线程和用户线程同时运行时,仍能相对准确地标记出所有存活对象,避免误删正在使用的对象。它不依赖全局暂停(STW),而是靠颜色状态 + 写屏障机制协同保障正确性。
三种颜色的实际含义与流转逻辑
颜色不是真实存储在对象头里的字段,而是一种逻辑状态,用于刻画对象在可达性分析过程中的扫描进度:
- 白色:尚未被 GC 访问过,初始全白;标记结束时仍为白色,即判定为不可达,可回收。
- 灰色:已被访问,但其直接引用的对象尚未全部扫描完;它是“待处理队列”的入口,GC 当前正从它出发继续遍历。
- 黑色:已被访问,且它所引用的所有对象也都已进入灰色或黑色;黑色对象绝不能直接指向白色对象——这是算法安全的关键约束。
并发场景下为何会出现漏标?
当用户线程修改引用关系时,可能打破“黑→白不可达”这一约束,从而导致本该存活的对象被漏掉。漏标必须同时满足两个条件:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 用户线程向一个黑色对象插入了指向白色对象的新引用(比如
a.field = whiteObj); - 同时,该白色对象原有的、来自灰色对象的引用被删除(比如
grayObj.field = null)。
此时,白色对象既无路径从灰色对象抵达,又因黑色对象不会被重新扫描,就彻底“消失”在图中,最终被错误回收。
主流回收器如何拦截漏标?
不同回收器采用不同写屏障策略,在引用变更发生时主动干预,破坏上述两个条件之一:
- CMS 使用增量更新(Incremental Update):一旦发现黑色对象新增了对白色对象的引用,就立刻将该黑色对象“降级”为灰色,后续重新扫描它——相当于补上遗漏的路径。
- G1 使用原始快照(SATB, Snapshot-At-The-Beginning):在标记开始时记录下所有对象引用关系的快照;之后若灰色对象删除了对白色对象的引用,就将该删除动作记录下来,等并发标记结束后,以这些被删引用的源对象为根再扫描一次——确保不丢失原快照中可达的对象。
为什么浮动垃圾可以容忍?
多标(即把本该回收的对象标记为存活)只影响效率,不破坏正确性。例如:E 已被标记为灰色,随后用户线程执行 e.field = null,但 E 及其子图仍会被继续扫描并标黑。这部分对象本轮不回收,变成“浮动垃圾”,留待下次 GC 处理即可。它不会引发 NPE 或数据错乱,属于设计上的合理折中。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










