三色标记法仅可视化并发标记状态,漏标需同时满足黑→白新增引用和灰→白路径断裂,cms用增量更新、g1用satb、zgc/shenandoah用读屏障堵漏;错标即浮动垃圾,无需解决;unsafe、final反射、逃逸分析等绕过屏障操作会导致漏标。

三色标记法本身不解决漏标或错标,它只是把并发可达性分析的过程可视化为白、灰、黑三种状态。真正起作用的是配套的写屏障机制和收集器策略。
漏标必须同时满足两个条件
- 黑色对象新增指向白色对象的引用(比如
a.field = new X(),而X还没被任何灰色对象发现) - 原有通向该白色对象的灰色路径被切断(比如原本
b → X,此时b.field = null,且b后续不再扫描)
单独发生任一操作都不会导致漏标:新增引用会被写屏障捕获;断开引用若仍有其他路径可达,X 就不会变白。
不同收集器用不同方式堵住漏标
- CMS 用增量更新(IU):赋值后检查,若发现黑→白引用,立即将源对象(如
a)重标为灰色,推回标记队列重新扫描 - G1 默认用 SATB(原始快照):赋值前记录旧引用(如
b → X即将消失),把X记入 SATB 缓冲区,Remark 阶段补扫 - ZGC / Shenandoah 用读屏障 + 转发指针:所有对象访问都经过屏障,自动覆盖引用变更,无需用户干预
错标(即多标/浮动垃圾)不用“解决”,只接受
比如一个对象刚被标黑,用户线程立刻丢弃了所有引用,它就成了浮动垃圾。这类对象本次不会被回收,但下轮 GC 必然清理——不影响正确性,只略降效率。
绕过屏障的操作仍可能漏标
-
Unsafe.putObject()直接写内存,跳过 Java 层引用管理 -
final字段在构造器外被反射或 Unsafe 修改 - JIT 逃逸分析后做标量替换,引用关系未落地,屏障无处生效
这些场景需避免,否则再强的写屏障也无效。
不复杂但容易忽略










