Skip to content

为什么CMS垃圾回收器被废弃并移除

CMS(Concurrent Mark Sweep)作为一款曾经非常流行的低延迟垃圾回收器,在JDK 9中被标记为废弃,并在JDK 14中被正式移除。

这一决定有多方面的原因:

CMS的核心缺陷

1. 内存碎片问题

CMS采用标记-清除算法,而不是标记-整理或复制算法。这导致:

  • 长期运行后产生大量内存碎片
  • 当碎片过多时,可能出现大对象分配失败的情况(即使总体上有足够的内存)
  • 最终可能触发Full GC进行内存整理,造成长时间停顿,违背低延迟的设计目标

CMS提供了-XX:+UseCMSCompactAtFullCollection-XX:CMSFullGCsBeforeCompaction参数来控制碎片整理,但这些方法都需要在Full GC时执行,仍然会导致较长停顿。

2. 并发失败风险

CMS的并发特性使其面临以下风险:

  • 并发模式失败(Concurrent Mode Failure):当并发收集过程中,老年代填满速度快于CMS收集速度时,会触发一次串行的Full GC
  • 晋升失败(Promotion Failed):当Survivor区放不下,需要晋升到老年代时,如果老年代空间不足,也会触发Full GC

这些情况下,CMS会退化为使用单线程的Serial Old收集器进行垃圾回收,导致极长的停顿时间,严重影响系统性能。

3. CPU资源敏感

CMS为了尽可能减少停顿,会与应用线程并发执行,这意味着:

  • CMS默认会占用系统CPU资源的1/4(JDK 5)或1/(n+3)(JDK 6及以后,n为CPU核数)
  • 在CPU核心较少的系统上,CMS会显著影响应用性能
  • 对计算密集型应用,影响尤为明显

4. 浮动垃圾问题

因为CMS在并发标记和并发清理阶段用户线程还在运行,这会导致:

  • 标记完成后又有新的垃圾产生,这部分被称为"浮动垃圾"
  • 这些垃圾只能等到下一次GC才能被清理
  • CMS需要预留一部分老年代空间给用户线程使用

5. 算法复杂,代码维护成本高

CMS实现非常复杂:

  • 有大量的并发处理代码
  • 需要处理各种边缘情况和异常情况
  • 代码维护难度大,bug修复成本高

G1对比CMS的优势

HotSpot团队开发G1垃圾回收器的主要目标就是替代CMS。G1解决了CMS的许多问题:

  1. 空间整合能力:G1具有内存压缩能力,能有效避免内存碎片问题
  2. 可预测的停顿模型:G1允许用户指定期望的停顿时间,并尽力满足
  3. 更灵活的堆内存管理:使用Region概念代替固定的分代物理空间
  4. 增量式垃圾回收:每次只处理一部分Region,避免全堆扫描

战略决策因素

Oracle做出废弃CMS的决策还有一些战略层面的考虑:

  1. 维护成本:同时维护多种GC算法意味着更高的开发和测试成本
  2. 资源聚焦:将资源集中在G1和ZGC等新一代收集器的开发上
  3. 技术迭代:随着硬件发展(多核、大内存),新的GC算法能提供更好的性能
  4. 统一路线:简化JVM配置选项,为用户提供更清晰的最佳实践

官方声明

JEP 291(Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector)中明确表示:

"CMS垃圾收集器已经过时了。G1收集器是一个功能齐全的收集器,旨在替代CMS,具有类似的并发收集目标,无需像CMS那样进行太多性能调优就能获得合理的行为。"

迁移建议

对于原先使用CMS的系统,官方建议:

  • 短期:升级到G1收集器(-XX:+UseG1GC
  • 中长期:评估ZGC或Shenandoah等低延迟收集器是否更适合业务需求

结论

CMS被废弃并最终移除,是JVM演进过程中的一个必然结果。虽然CMS在其时代是一个优秀的低延迟收集器,但随着G1和ZGC等更先进技术的出现,它的各种固有缺陷(内存碎片、并发失败风险、复杂的维护成本等)使其不再是最佳选择。Oracle的这一决定代表了垃圾回收技术向着更低延迟、更高吞吐量、更易使用的方向发展的趋势。