CMS收集器采用标记-清除算法,不移动对象,导致老年代内存碎片化;碎片积累引发Concurrent Mode Failure并触发Full GC,形成恶性循环;可通过参数启用Full GC时的压缩整理,或调优新生代减少对象过早进入老年代;推荐升级至G1、ZGC或Shenandoah等现代收集器以根本规避碎片问题。

CMS 收集器本身不解决内存碎片问题,它采用标记-清除算法,回收后只释放空间、不移动对象,必然产生大量不连续的空闲内存。碎片积累到一定程度,即使总空闲空间足够,也可能因找不到足够大的连续区域而触发 Full GC。
CMS 的碎片本质与局限
CMS 的设计目标是降低停顿时间,为此牺牲了内存整理能力。它在并发清理阶段只是把被标记为垃圾的对象所占空间加入空闲链表(free list),并不压缩或移动存活对象。这意味着:
- 老年代内存会随多次 CMS GC 逐渐“千疮百孔”
- 大对象分配失败 → 触发 Concurrent Mode Failure → 回退为 Serial Old 或 Parallel Old 的 Full GC
- 频繁 Full GC 又进一步加剧碎片恶性循环(尤其在高负载、大对象场景下)
依赖 Full GC 时的压缩补救
CMS 本身不整理,但可通过参数在 Full GC 时强制执行内存压缩:
-
-XX:+UseCMSCompactAtFullCollection:启用 Full GC 后的内存整理 -
-XX:CMSFullGCsBeforeCompaction=N:指定每 N 次 Full GC 执行一次压缩(如设为 1,则每次 Full GC 都整理)
注意:该压缩由底层使用的 Full GC 收集器(如 Serial Old)完成,不是 CMS 自身行为;JDK 8 中默认使用 Serial Old,它采用标记-整理算法,能有效消除碎片。
规避碎片的主动调优策略
比等 Full GC 整理更高效的做法是减少对象过早进入老年代:
- 调大新生代(尤其是 Eden 和 Survivor 区),避免 Young GC 后存活对象溢出直入老年代
- 合理设置
-XX:MaxTenuringThreshold,延长对象在新生代的存活周期 - 监控
jstat -gc输出,重点关注每次 Young GC 后晋升到老年代的对象大小,据此调整 SurvivorRatio 和堆比例 - 避免显式调用
System.gc(),防止意外触发 Full GC 干扰碎片节奏
替代方案:升级到更现代的收集器
CMS 在 JDK 9 被标记为废弃,JDK 14 正式移除。当前推荐用以下替代方案从根本上规避碎片问题:
- G1 收集器:分区设计 + 标记-整理,在后台自动整理部分 Region,兼顾停顿可控与低碎片
- ZGC / Shenandoah:真正并发整理,几乎无停顿,彻底摆脱碎片困扰
若仍在维护旧系统,应优先通过参数控制 Full GC 整理频率,并结合新生代调优从源头压制碎片生成速度。


















