三色标记法通过白、灰、黑三种颜色区分对象扫描状态,核心约束是“黑→白引用不可存在”,浮动垃圾是并发标记下因栈帧销毁或新对象直接标黑导致的本轮漏回收对象,属可控权衡。

三色标记法是并发垃圾回收中用于解决“边标记、边运行”问题的核心机制,它通过颜色状态区分对象的扫描进度,避免全局停顿(Stop-the-World)。理解它的关键不在于记颜色,而在于看清状态流转逻辑和并发干扰点。“浮动垃圾”正是这种并发性带来的自然副产品,不是缺陷,而是权衡延迟与正确性的结果。
三色标记的本质:三个集合 + 一个不变约束
所谓“三色”,对应的是三个逻辑集合,背后是一套状态迁移规则:
- 白色对象:尚未被 GC 线程访问过,处于“待定”状态;若标记结束仍为白色,即判定为不可达,可安全回收。
- 灰色对象:已被访问,但其直接引用的对象尚未全部检查完毕;它是 GC 工作队列的主体,标记线程持续从中取对象处理。
- 黑色对象:自身及所有直接引用的对象都已扫描完成;它被视为“已确认存活”,GC 不再重新扫描它。
算法正确性的核心约束是:黑色对象不能直接引用白色对象。一旦出现黑→白引用,就可能漏标——因为黑色对象不会再被扫描,而白色对象又未被任何路径触及,会被误判为垃圾。因此,所有写屏障(如 SATB 或增量更新)的设计目标,都是拦截或补偿这类破坏约束的操作。
浮动垃圾是怎么产生的?两种典型场景
浮动垃圾指本该本轮回收、却因并发标记的时序问题而“侥幸存活”的对象。它不破坏内存安全,只影响回收及时性。常见产生方式有两类:
- 栈帧提前销毁:方法执行结束,局部变量(作为 GC Root)从调用栈弹出,其所引用的对象瞬间变为不可达;但该对象在并发标记早期已被扫描并标记为灰色/黑色,后续不会被重新评估,只能留待下次 GC。
- 新分配对象被“保底标记”:在并发标记或并发清理阶段新创建的对象,JVM 通常直接设为黑色(视为本轮必存活),避免复杂追踪;但若这些对象很快失去所有引用,它们就成了本轮无法回收的浮动垃圾。
为什么浮动垃圾可以被接受?
浮动垃圾的存在,本质是 JVM 在“低延迟”和“100%即时回收”之间做的务实选择:
- 它不导致内存泄漏,下一轮 GC 必然识别并回收;
- 它避免了为消灭少量浮动垃圾而引入更重的同步开销或更长的 STW;
- 在 CMS、G1 等收集器中,浮动垃圾率通常可控(
换句话说,浮动垃圾不是 bug,是并发标记的“合理代价”。真正需要严防的是漏标——那会直接引发 NullPointerException 或数据错乱。










