news 2026/9/8 17:25:52

06-JVM垃圾回收器之CMS

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
06-JVM垃圾回收器之CMS

本篇是「JVM 与性能调优系列」第 6 篇。

G1 之前,CMS 是低延迟的代名词。它敢在程序运行的同时回收老年代。但它也留下了「碎片化的诅咒」,最终被官方废弃。看懂 CMS,才懂 G1 为什么是答案。


一、CMS 要解决什么

Parallel Old 回收老年代时是全程 STW,堆一大,停顿动辄几百毫秒——对 Web 服务是灾难。CMS(Concurrent Mark Sweep)的目标很明确:降低老年代回收的 STW,让大部分回收工作和业务线程并发进行

核心思想:把「能并发的尽量并发」,只保留极短的 STW 段。代价是算法更复杂、有碎片副作用。


二、CMS 的四个阶段

CMS 的老年代回收分四步,其中两步极短 STW,两步完全并发:

  1. 初始标记(STW,极短):只标记 GC Roots直接关联的对象(数量少,快)
  2. 并发标记(并发):从初始标记的对象出发,沿引用链遍历整个老年代。这段时间业务线程照常跑
  3. 重新标记(STW,较短):并发标记期间,业务线程可能改了引用关系,导致标记有偏差。这一步把变动修正过来(用「增量更新」)
  4. 并发清除(并发):清除死亡对象,用标记-清除算法,不挪动存活对象

注意第 2、4 步是并发的,所以业务几乎不感知——这正是 CMS 低延迟的来源。但「重新标记」阶段仍要 STW,它扫的是并发期间变动的引用,通常比初始标记略长。


三、三色标记与重新标记

并发标记用三色标记追踪存活:

  • 白:尚未访问(可能是垃圾)
  • 灰:已访问自身,但引用还没扫完
  • 黑:自身和引用都扫完,确定存活

问题来了:并发标记时,业务线程可能「把一个白对象挂到黑对象下,同时断开灰对象到它的引用」——白对象会被误判为垃圾(漏标)。

CMS 用**增量更新(Incremental Update)**补救:只要黑对象新插入了白对象的引用,就把它记下来,重新标记时重新扫。G1 换了个思路用 SATB(下一篇讲)。两种都是「并发标记如何不漏标」的解法。


四、CMS 的两大原罪

CMS 证明了并发回收的价值,却栽在两个硬伤上:

原罪 1:碎片。CMS 用标记-清除,不清空后不整理,老年代会留下越来越多空洞。某天要分配一个大对象,总空闲够却找不到连续空间 → 晋升失败 →退化成 Serial Old 做一次 Full GC(巨慢)

原罪 2:并发失败。并发清除期间,业务还在不停 new 对象,老年代被填充。若填充速度超过清除速度、预留空间(默认 92% 触发)被击穿 →Concurrent Mode Failure→ 同样退化成 Serial Old。


五、关键参数

参数作用
-XX:+UseConcMarkSweepGC启用 CMS
-XX:CMSInitiatingOccupancyFraction老年代占用到多少触发 CMS(默认 92%)
-XX:+UseCMSCompactAtFullCollectionFull GC 时做碎片整理
-XX:CMSFullGCsBeforeCompaction每隔几次 Full GC 整理一次
-XX:+CMSParallelRemarkEnabled重新标记阶段并行,缩短 STW

调优要点:如果频繁 Concurrent Mode Failure,把CMSInitiatingOccupancyFraction调低(比如 70%),让 CMS 更早启动;但太低会 GC 太频繁。


六、为什么被废弃

  • JDK 9 标记为废弃,JDK 14彻底移除
  • 维护成本高、碎片化无解、无法与新特性(如 ZGC 的染色指针)共存
  • 官方明确推荐迁移到G1(JDK 9 起默认)

今天新项目基本不该用 CMS 了,但理解它仍重要——很多老系统还在跑,而且 G1 的设计正是对 CMS 缺陷的回应。


七、承上启下

CMS 用「并发标记 + 标记清除」把老年代停顿降下来,却败给碎片。G1 的解决方案是:把堆切成小 Region,每次只回收价值最高的 Region,并在回收时做整理——既并发,又无碎片。


八、实战:一次 Concurrent Mode Failure 复盘

某电商服务,老年代 4G,CMSInitiatingOccupancyFraction=92。大促时突发流量,对象以 300MB/s 的速度晋升老年代。CMS 在 92%(约 3.7G)才启动并发标记,但标记+清除速度跟不上业务填充速度,老年代很快冲破上限 → 触发Concurrent Mode Failure→ 退化 Serial Old 全堆整理 →一次 2.3 秒的全局停顿,接口全部超时。

根因不是 CMS 不行,是触发阈值太高。把CMSInitiatingOccupancyFraction调到 70%,让 CMS 提前启动,并发阶段有充足时间跟上业务速度,问题消失。


九、CMS 与日志的对应

开启-XX:+PrintGCDetails后,CMS 的周期在日志里长这样:

CMS-initial-mark → CMS-concurrent-mark → CMS-concurrent-preclean → CMS-remark (STW) → CMS-concurrent-sweep → CMS-concurrent-reset

看到concurrent mode failure字样,就对应上面的原罪 2,优先调低触发阈值。


十、CMS 废弃后的迁移路径

如果你的老系统还在用 CMS(-XX:+UseConcMarkSweepGC),JDK 14+ 会直接启动失败。迁移建议:

  1. 首选 G1:JDK 9+ 默认,几乎无需改业务代码,停顿通常比 CMS 更稳、无碎片。多数 CMS 应用平迁 G1 即可
  2. 低延迟需求迁 ZGC:若对停顿极敏感且能升 JDK 17/21,ZGC 是终极答案
  3. 保留 Parallel 的场景:纯吞吐批处理,且不想动 JDK,Parallel 也完全够用

迁移不是换个参数那么简单:要先在测试环境压测,对比停顿与吞吐,确认新收集器在你的真实负载下达标,再上生产。别信「换个收集器自动变快」的神话。


十一、CMS 并发模式失败深度剖析

Concurrent Mode Failure是 CMS 最典型的崩溃,值得深挖它的触发链:

  1. CMS 在老年代占用到CMSInitiatingOccupancyFraction(默认 92%)时才启动并发周期
  2. 并发标记 + 并发清除期间,业务线程仍在疯狂分配,老年代持续被填充
  3. 如果填充速度 > 清除速度,老年代在周期结束前就见了底
  4. CMS 无力回天 → 冻结所有线程 → 退化成Serial Old做一次全堆、单线程、标记-整理的 Full GC

这次退化 Full GC 可能耗时数秒(堆越大越久),期间所有请求超时。根因不是 CMS 慢,是触发太晚 + 分配太快。把CMSInitiatingOccupancyFraction调到 70%~80%,让并发周期早启动、留足缓冲,就能避免。


总结

CMS 是低延迟的先驱:初始标记(STW) → 并发标记 → 重新标记(STW) → 并发清除,把大部分工作并发化。但它用标记-清除留下碎片,并发期间又可能并发失败,两者都逼它退化成 Serial Old。它的退役,恰好引出了下一篇的主角 G1。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 17:25:46

开源免费远程桌面软件RustDesk的安装与使用

RustDesk是免费开源的远程桌面软件,支持Windows、Mac、Linux、Androd等系统,可以跨不同系统进行远程桌面访问。可以穿透内网,不论是单位内网电脑还是家里电脑,都可以远程访问。在低带宽网络环境中也能保持高速传输,电脑…

作者头像 李华
网站建设 2026/9/8 17:23:20

IntelliJ IDEA 社区版:从源码到跑通 IDE 的3步路径

IntelliJ IDEA 社区版:从源码到跑通 IDE 的3步路径 【免费下载链接】intellij-community IntelliJ IDEA & IntelliJ Platform 项目地址: https://gitcode.com/GitHub_Trending/in/intellij-community 如果你曾想弄清代码补全这类功能内部到底怎么实现&am…

作者头像 李华
网站建设 2026/9/8 17:22:41

国产MCU嵌入式开发实战:从选型到量产全流程体验

一个小项目,让我重新认识了国产MCU,有些话不吐不快 先说结论:这次用国产MCU做完整个项目,我是真的服气了。以前总觉得国产芯片就是“能用但折腾”,数据手册写得像机翻、开发工具链各种别扭、出了问题社区里连个提问的人…

作者头像 李华
网站建设 2026/9/8 17:19:36

GitNexus架构拆解:如何让AI写代码变得可控可回滚

先聊个让人又爱又恨的现状:AI写代码已经成了不少团队的日常,可它改崩代码也是真有一手。你让它“顺手优化一下这个函数”,回来一看,依赖关系乱了、边界条件丢了、连项目原本的风格都给你改得面目全非。更头疼的是,这种…

作者头像 李华
网站建设 2026/9/8 17:19:19

Spring Boot毕设:区域酒店住宿系统库存与订单链路方案

“区域酒店住宿信息系统”这类毕设标题,乍看就是一套普通的 CRUD 管理系统,很多同学拿到选题的第一反应是“做几个页面,增删改查一下就完事”。但真正动手之后才发现问题全在后面:区域怎么存、房型库存怎么算、两个用户同时订同一…

作者头像 李华
网站建设 2026/9/8 17:18:28

深入ARM Compute Library:从构建体系到NEON与OpenCL优化实践

1. 工程全景:先搞清楚 ARM Compute Library 到底在解决什么问题如果你做过嵌入式或移动端的高性能计算,多半听过 ARM Compute Library(简称 ACL)。这是 ARM 官方开源的一套计算库,针对 ARM 平台深度优化,提…

作者头像 李华