浮动垃圾是cms为降低停顿时间所付出的代价,指并发标记结束后新产生且未被本轮标记覆盖的不可达对象,需待下一轮gc根据其可达性状态决定是否回收。

CMS 垃圾回收器中的“浮动垃圾”是指在并发标记阶段结束后、到并发清除阶段完成前,应用程序线程新产生的、且在清除开始时已不可达、但未被本次 CMS 回收的对象。
为什么会产生浮动垃圾
CMS 是并发收集器,标记和清除过程与用户线程交叉执行。关键点在于:
- 标记阶段采用三色标记(白灰黑),但只做一次初始快照式的可达性分析;
- 标记完成后,应用线程仍在运行,可能修改引用关系(如断开旧引用、建立新引用);
- 这些变动可能导致某些对象在标记结束时仍被误认为“存活”,实际已不可达;
- 而 CMS 不会重新扫描——它只清理“未被标记”的白色对象,因此这部分真实垃圾被跳过,成为“浮动垃圾”。
浮动垃圾的特点和影响
浮动垃圾不是错误,而是 CMS 为降低停顿时间所付出的代价:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 它不会导致内存泄漏,会在下一次 CMS 循环或 Full GC 中被回收;
- 但它会占用老年代空间,加速老年代填满;
- 若浮动垃圾过多,可能触发“并发模式失败(Concurrent Mode Failure)”,迫使 JVM 回退到 Serial Old 进行 STW 的 Full GC,造成明显卡顿。
如何应对浮动垃圾
无法完全消除,但可通过配置和监控主动控制其影响:
-
提前启动 CMS:用
-XX:CMSInitiatingOccupancyFraction=n设置老年代使用率阈值(如 70),避免等堆快满才启动,留出空间容纳浮动垃圾; -
启用增量更新:CMS 默认开启增量更新(
-XX:+CMSScavengeBeforeRemark可辅助减少 remark 阶段压力),配合写屏障捕获并发修改,缓解漏标; -
预留安全空间:通过
-XX:CMSFullGCsBeforeCompaction(已废弃)或更实际的手段——适当增大老年代,或搭配 ParNew 调优新生代晋升行为,减少不必要的大对象直接进入老年代; -
监控与识别:观察 GC 日志中
concurrent mode failure出现频率,以及 CMS 周期后老年代剩余空间是否持续收缩,这是浮动垃圾积累的信号。
浮动垃圾 vs 内存泄漏
需注意区分:浮动垃圾是短暂存在的、可被后续 GC 清理的正常现象;而内存泄漏是对象本该被回收却因强引用残留长期驻留。若 CMS 后老年代占用率只升不降,应优先排查泄漏,而非归咎于浮动垃圾。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










