三色标记算法防漏标的核心是禁止黑色对象直接指向白色对象,否则存活对象可能被误回收;漏标源于用户线程与gc线程并发修改引用;增量更新将新指向白色的黑色对象重标为灰色,satb则基于初始快照保障引用关系不丢失。

三色标记算法在并发垃圾回收中防范漏标,核心是守住一条铁律:黑色对象不能直接指向白色对象。一旦打破,就可能把本该存活的对象漏掉——它没被任何灰色或黑色对象“拉住”,最终被当成垃圾回收,程序直接崩溃。
为什么漏标会发生?
因为用户线程和 GC 线程同时运行:
- GC 线程正把 ObjA(灰色)扫描完,标记为黑色,接着去处理 ObjB;
- 此时用户线程执行了两件事:删掉 ObjB → ObjC 的引用,又让 ObjA → ObjC 新建引用;
- ObjC 原本靠 ObjB 活着,现在 ObjB 断了它,而 ObjA 已经是黑色,不会再被扫描——ObjC 就彻底“隐身”了,全程白着,最后被回收。
两种主流防御方案怎么补漏?
它们都依赖写屏障(WriteBarrier),也就是在每次对象引用赋值时悄悄插一段逻辑,捕获变动:
- 增量更新(Incremental Update):发现黑色对象新指向白色对象,立刻把那个黑色对象“打回原形”变回灰色,后续重新扫描它——相当于说:“你刚改了引用,那之前标过的不算数,再走一遍。”
- 原始快照(SATB, Snapshot-At-The-Beginning):在并发标记开始前,先记下所有灰色对象当时的引用快照;之后哪怕用户线程删了某个引用,GC 仍按快照里“原本有”的关系去检查——相当于说:“我不管你怎么删,我只认一开始你指着谁。”
重新标记阶段干啥?
它不是从头再来,而是收尾查漏:
- 把写屏障记录下来的“新增引用”或“删除动作”拿出来处理;
- 快速扫一遍那些还没处理完的灰色对象;
- 修正因并发导致的少量偏差,确保所有存活对象至少被标成灰色或黑色。
漏标和浮动垃圾的区别要拎清
漏标是致命错误,必须防住;浮动垃圾只是多标了——比如一个对象刚被创建就被丢弃,但 GC 还没来得及发现,它被标黑了,这次没回收,等下次 GC 再清理。这不伤程序,只影响效率。










