漏标是并发标记中真实可复现的问题,需同时满足“黑色对象新增指向白色对象的引用”和“灰色对象断开指向该白色对象的引用”两个条件;cms用增量更新(赋值后将黑对象标灰),g1用satb(赋值前记录原引用快照)来拦截并补偿。

三色标记漏标风险不是理论假设,而是并发标记中真实可复现的问题。核心在于:用户线程修改引用时,GC线程按既定路径推进,两者节奏错开,就可能让一个本该存活的对象“滑出”扫描范围。掌握它,关键不是背条件,而是看清对象颜色状态如何随引用变动而失衡,再理解写屏障如何在关键节点“踩刹车+补动作”。
用代码还原漏标发生的完整链条
下面这段精简示例能直接触发漏标逻辑(JVM参数需开启CMS或G1并发标记):
class A { B b = new B(); }
class B { D d = new D(); }
class D { }
public class LeakDemo {
static A a = new A();
public static void main(String[] args) throws InterruptedException {
// 初始:a → b → d,d为白色
// GC线程开始并发标记,已将a、b标为灰色,正准备扫描b.d
Thread gcSimulator = new Thread(() -> {
try { Thread.sleep(10); } catch (InterruptedException e) {}
// 假设此时GC刚把b标黑(b.d已扫完),a也变黑
System.out.println("GC: a and b now BLACK");
});
Thread appThread = new Thread(() -> {
try { Thread.sleep(5); } catch (InterruptedException e) {}
// 条件1:黑色a新增对白色d的引用(a.d = d)
a.d = a.b.d;
try { Thread.sleep(2); } catch (InterruptedException e) {}
// 条件2:灰色b断开对d的引用(b.d = null)
a.b.d = null;
System.out.println("App: a.d = d; then b.d = null");
});
gcSimulator.start();
appThread.start();
gcSimulator.join(); appThread.join();
// 此时d仅被黑色a引用,但a不会再被重新扫描
// d未被任何灰色对象带入队列 → 保持白色 → 被回收
}
}
运行后若d被错误回收,就会在后续访问时报 NullPointerException。这个过程清晰暴露两个必要动作:黑→白引用插入 + 灰→白路径切断。缺一不可。
为什么必须同时满足两个条件?
单看任一动作都不足以导致漏标:
- 只新增黑→白引用:比如a.d = d,但b.d仍存在 → d仍可通过灰色b被发现,不会漏
- 只切断灰→白引用:比如b.d = null,但a没引用d → d彻底无引用,本就是垃圾,回收正确
只有两者叠加,才让d陷入“无人可达”的真空状态:黑色对象不回头,灰色对象已断链,白色对象静默消失。
CMS与G1应对策略的本质差异
两者都靠写屏障拦截引用变更,但拦截时机和补偿逻辑完全不同:
- CMS用增量更新(Incremental Update):监听赋值动作本身。一旦发现 a.d = d(黑→白),立刻把a重标为灰色,下次重新扫描a及其全部引用。代价是可能重复扫描,但保证不漏
- G1用SATB(Snapshot-At-The-Beginning):监听旧引用失效前一刻。当b.d = null执行前,写屏障捕获“b原指向d”这一快照,记入SATB缓冲区。最终标记阶段,GC会顺着这个快照把d重新拉回灰色队列。不改颜色,只保路径
简单说:CMS是“你动了引用,我就重查你”;G1是“你删引用前,我先拍个照,按照片找人”。
日常开发中规避引用风险的实操建议
漏标是GC层问题,但应用层引用操作习惯直接影响其发生概率:
- 避免在临界区内高频修改长引用链:如 obj.a.b.c = null; obj.x.y = obj.a.b.c; 这类操作易与GC扫描窗口重叠。尽量拆成原子步骤,或加锁同步
- 警惕“临时强引用”模式:比如缓存中先存弱引用,业务需要时转强引用。这种切换若发生在并发标记期,极易触发黑→白赋值。建议统一用SoftReference或配合显式清理
- 日志与监控关注Final Remark耗时突增:CMS中该阶段要处理所有增量更新记录;G1中要遍历SATB缓冲区。耗时飙升往往意味着近期引用变动剧烈,是潜在风险信号










