堆内存碎片化会阻碍大对象分配并触发频繁gc或oom;标记-清除类(如cms)不移动对象、留碎片;复制类(如serial、g1年轻代)天然无碎片但空间减半;标记-整理类(如g1混合回收、zgc、shenandoah)边回收边压缩,兼顾空间利用率与低碎片。

堆内存碎片化会影响大对象分配,导致频繁触发GC甚至OOM。不同垃圾回收器通过底层算法差异,对碎片问题采取了截然不同的应对策略。
标记-清除类回收器:不处理碎片,只留隐患
CMS 是典型代表。它在老年代使用标记-清除算法:先标记存活对象,再直接清理未标记区域。这种做法不移动对象,所以停顿短、响应快,但会留下大量不连续的空闲块。当需要分配一个较大对象(比如 4MB)时,即使总空闲内存充足,也可能因找不到连续空间而失败,被迫触发 Full GC。这也是 CMS 在 JDK 9 后被弃用的重要原因之一。
复制类回收器:天然无碎片,代价是空间减半
Serial、Parallel Scavenge 和 G1 的年轻代回收都采用复制算法。它们把内存划为 Eden + Survivor(或 G1 中的多个 Region),GC 时只把存活对象复制到另一块空闲区域,原区域整块清空。结果是每次回收后,目标区域始终紧凑连续,完全规避碎片。缺点也很明显:必须预留等量备用空间,实际可用堆容量打折扣;且对象存活率高时,复制开销陡增。
标记-整理类回收器:边回收边压缩,主动消除碎片
G1、Parallel Old、ZGC 和 Shenandoah 都在老年代或全堆层面引入了整理逻辑。G1 在混合回收阶段会选定若干 Region,将其中存活对象复制到其他 Region,腾出整块干净空间;Parallel Old 则直接将老年代存活对象向一端滑动压缩;ZGC 和 Shenandoah 更进一步,在应用线程运行的同时并发完成对象移动与引用更新,实现“零停顿+无碎片”。这类方式不牺牲空间利用率,但需额外处理对象移动和指针重定向,带来一定 CPU 开销。
选择建议:看场景,不唯参数
– 如果应用对延迟极度敏感(如高频交易、实时风控),且堆不大,ZGC 或 Shenandoah 是首选,它们能持续维持低碎片率;
– 如果追求吞吐量、堆较大(>32GB)、可接受百毫秒级停顿,G1 更均衡;
– 如果系统老旧、堆较小(
– 新项目避免使用 CMS,它不解决碎片,反而因碎片加剧 GC 压力。











