三色标记算法旨在支持并发标记以大幅压缩stw时间,而非替代标记-清除;它通过白(未访问)、灰(已访问未扫完)、黑(已访问且子引用全扫描)三色状态显式跟踪对象扫描进度,配合写屏障与重新标记解决并发漏标问题。

三色标记算法不是为“替代”传统标记-清除而生,而是为了让标记阶段能和用户线程并发执行,大幅压缩 Stop-The-World(STW)时间。它本身不负责清扫,只解决“哪些对象存活”这个核心判定问题;真正的内存回收(清扫或整理)在标记完成后才发生,且通常仍需少量 STW 或完全并发进行。
三色标记的核心角色:给并发标记装上状态“仪表盘”
它用三种颜色把堆中每个对象的扫描进度显式编码出来:
- 白色:还没被 GC 看过——不代表是垃圾,只是“待查”。标记结束时还是白的,才算真垃圾。
- 灰色:GC 已经访问过它,但它的字段还没扫完——它是当前扫描的“活口”,所有待处理引用都从这里发散出去。
- 黑色:本体 + 所有直接引用都已确认——它是“已结案”的存活对象,GC 绝不再碰它,且它不能直接连向白色对象(否则就漏标)。
并发标记怎么跑起来:GC 线程与用户线程“错峰协作”
整个过程分阶段推进,关键不在“全速扫完”,而在“边扫边控风险”:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 初始标记(STW):极短暂停,只把 GC Roots 直接引用的对象涂灰,入队。
- 并发标记(主要阶段):GC 线程不断从灰色队列取对象,涂黑自己,把其未标记的子对象涂灰;同时用户线程照常运行、修改引用。
- 写屏障介入:一旦用户线程执行
obj.field = newObj或obj.field = null,JVM 自动触发写屏障逻辑——CMS 记下“黑→白”新引用,G1 记下“灰→白”被删的旧引用。 - 重新标记(STW):快速回溯写屏障记录的“危险变更”,以对应对象为根补扫,修正可能漏掉的存活路径。
标记之后怎么清扫:三色不管清理,但决定清理什么
三色标记只输出一张“存活名单”(黑)和“待清名单”(白),后续清扫策略由具体回收器决定:
- G1 使用“标记-整理”思路:把存活对象复制到新区域,原地腾空——既避免碎片,又支持增量回收。
- ZGC / Shenandoah 采用“并发整理”:移动对象的同时更新所有引用,全程几乎无 STW。
- CMS 则走“标记-清除”老路:直接清掉白色对象,留下碎片,靠下次分配时的空闲链表管理。
为什么会有漏标?本质是并发下的“状态竞态”
漏标只在两个动作**同时发生**时出现:
- 一个灰色对象删掉了对某个白色对象的引用;
- 另一个黑色对象新增了对该白色对象的引用。
这时白色对象“失联又重连”,但因黑色对象不再扫描,它就被误判为垃圾。CMS 用增量更新(盯住黑→白新增),G1 用 SATB(快照灰→白删除前的状态),都是为堵住这个窗口。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










