Appearance
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的许多问题:
- 空间整合能力:G1具有内存压缩能力,能有效避免内存碎片问题
- 可预测的停顿模型:G1允许用户指定期望的停顿时间,并尽力满足
- 更灵活的堆内存管理:使用Region概念代替固定的分代物理空间
- 增量式垃圾回收:每次只处理一部分Region,避免全堆扫描
战略决策因素
Oracle做出废弃CMS的决策还有一些战略层面的考虑:
- 维护成本:同时维护多种GC算法意味着更高的开发和测试成本
- 资源聚焦:将资源集中在G1和ZGC等新一代收集器的开发上
- 技术迭代:随着硬件发展(多核、大内存),新的GC算法能提供更好的性能
- 统一路线:简化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的这一决定代表了垃圾回收技术向着更低延迟、更高吞吐量、更易使用的方向发展的趋势。