CMS无法避免内存碎片化,因其采用标记-清除算法,只释放死亡对象空间而不移动存活对象;缓解手段包括提前触发回收、Full GC时压缩、调优年轻代及低峰期主动整理。

CMS 收集器本身不避免内存碎片化,反而会加剧碎片化——因为它采用标记-清除(Mark-Sweep)算法,只回收死亡对象的空间,但不移动存活对象,也不整理内存。所以它无法从根本上避免碎片,只能通过辅助手段缓解碎片带来的影响。
下面从原理和实践两个层面说明 CMS 如何应对碎片问题:
CMS 为什么必然产生内存碎片
- 清理阶段只是将未被标记的对象内存“释放”,加入空闲链表(free-list),不进行任何对象移动或空间压缩;
- 长期运行后,老年代中会出现大量不连续的小块空闲空间;
- 当需要分配大对象(如大数组、缓存块)时,即使总空闲内存足够,也可能因缺乏连续空间而失败,触发 Full GC(通常由 Serial Old 执行,停顿时间长)。
缓解碎片的实际方法
提前触发 CMS 回收
通过-XX:CMSInitiatingOccupancyFraction=N(如设为 70)降低老年代触发阈值,让 CMS 更早介入清理,减少碎片累积机会,同时合并相邻空闲块。在 Full GC 时强制压缩
启用-XX:+UseCMSCompactAtFullCollection,使每次 Full GC 执行标记-整理(Mark-Compact),把存活对象向一端靠拢,消除碎片。
可配合-XX:CMSFullGCsBeforeCompaction=N(如设为 1),表示每 N 次 Full GC 就做一次压缩。搭配年轻代调优减少晋升压力
增大年轻代(-Xmn)或调整 Survivor 区比例(-XX:SurvivorRatio),让短生命周期对象尽量在 Eden 或 Survivor 中被回收,减少进入老年代的中等寿命对象数量,间接降低老年代碎片生成速率。主动触发可控的 Full GC 整理
在业务低峰期(如凌晨)调用System.gc()(需配合-XX:+ExplicitGCInvokesConcurrent或-XX:+UseConcMarkSweepGC),促使 CMS 触发一次带压缩的 Full GC,重整老年代布局。
CMS 的设计目标是低延迟,不是低碎片。它用“不整理”换“不停顿”,所以碎片是其固有代价。真正想规避碎片,应考虑升级到 G1 或 ZGC——它们内置了区域化管理与自动整理机制。


















