JVMNotes

第 16 章:CMS

zjc 于 2026-01-16 发布

这是《JVM 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 CMS(Concurrent Mark Sweep)曾是主流低停顿老年代收集器,目标是让大部分标记和清理与应用并发执行。但它有内存碎片、并发失败、浮动垃圾和参数复杂等问题,HotSpot 在 Java 14 中移除了 CMS。

16.1 历史定位

Serial / Parallel
  -> 停顿随老年代增大变长
     -> CMS 引入并发标记清除
        -> G1 / ZGC / Shenandoah 继续演进

Java 版本变化:

版本 变化
Java 8 CMS 可用
Java 9 CMS 被标记 deprecated
Java 14 CMS 被移除

仍在使用 CMS 的系统通常是老 JDK 或存量平台,应规划升级。

16.2 工作阶段

Initial Mark
  -> Concurrent Mark
     -> Concurrent Preclean
        -> Final Remark
           -> Concurrent Sweep
              -> Concurrent Reset
阶段 STW 说明
Initial Mark 标记根直接可达对象
Concurrent Mark 遍历对象图
Preclean 预处理并发期间变化
Final Remark 处理剩余引用
Concurrent Sweep 清理死亡对象
Concurrent Reset 重置状态

16.3 优缺点

优点:

  1. 老年代标记清除大部分并发;
  2. 低停顿优于 Parallel Full GC;
  3. 适合当时交互服务。

缺点:

  1. mark-sweep 产生碎片;
  2. 并发阶段占用 CPU;
  3. 浮动垃圾;
  4. 需要预留空间;
  5. concurrent mode failure 会退化 Serial Old;
  6. 参数复杂;
  7. 维护成本高。

16.4 内存碎片

清除后空闲块不连续
  -> 总空闲足够
     -> 无法分配大对象
        -> 触发 Full GC / 压缩

碎片常见诱因:

  1. 大对象;
  2. 长期运行;
  3. 对象大小差异大;
  4. 晋升频繁;
  5. 碎片整理不及时。

CMS 有增量整理策略,但难以彻底消除碎片问题。

16.5 并发失败

常见日志概念:

concurrent mode failure
promotion failed

原因:

  1. 老年代预留不足;
  2. 晋升过快;
  3. 碎片导致空间不可用;
  4. 并发回收跟不上分配;
  5. CPU 不足。

后果通常是退化到更重停顿的 Full GC。

16.6 常见参数

-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=70
-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSMaxAbortablePrecleanTime=5000
-XX:CMSFullGCsBeforeCompaction=0

这些参数只适用于仍支持 CMS 的 JDK。设置 CMSInitiatingOccupancyFraction 太高会增加并发失败风险,太低会频繁并发周期。

16.7 与 G1 对比

CMS G1
堆布局 连续 Old 分代 Region 化
算法 标记清除 复制 / 整理
碎片 明显 通常更低
停顿可控 PausePredictionModel
大对象 直接 Old Humongous Region
维护 已移除 现代 JDK 持续演进

从 CMS 迁移到 G1:

  1. 升级 JDK;
  2. 保留 GC 日志和指标基线;
  3. 压测对比;
  4. 关注大对象和晋升;
  5. 检查内存占用变化;
  6. 分批灰度;
  7. 保留回滚。

16.8 排障思路

查看 GC 日志:

1. CMS 周期频率
2. Initial / Final Remark 耗时
3. Old 回收量
4. 晋升量
5. concurrent mode failure
6. Full GC 原因

处理:

现象 方向
并发周期频繁 分配高或阈值低
final remark 长 引用处理多
promotion failed Survivor / Old 不足
concurrent failure 预留不足或 CPU 不足
碎片化 大对象和长期运行

16.9 面试要点

CMS 常考问题:

  1. 初始标记和最终标记为什么 STW;
  2. 并发标记如何处理引用变化;
  3. 为什么会有浮动垃圾;
  4. 为什么 mark-sweep 会碎片化;
  5. 为什么并发失败停顿长;
  6. 为什么 CMS 被移除。

回答时应说明它是特定历史阶段的折中设计,而不是把 CMS 当作现代默认选择。

本章小结

CMS 用并发标记清除降低老年代回收停顿,但代价是碎片、浮动垃圾、CPU 竞争和并发失败风险。它已在 Java 14 被移除,现代 Java 服务应优先评估 G1、ZGC 或 Shenandoah。理解 CMS 有助于理解低停顿收集器的演进。

思考题

  1. CMS 哪些阶段需要 STW?
  2. 为什么 CMS 会产生浮动垃圾?
  3. concurrent mode failure 的常见原因是什么?
  4. CMS 为什么会产生内存碎片?
  5. 从 CMS 迁移到 G1 要验证哪些指标?