cms收集器以最短停顿为目标,通过初始标记、并发标记、重新标记、并发清除四阶段实现低延迟;仅两次毫秒级stw,其余阶段与用户线程并发执行,但存在cpu开销高和内存碎片问题。

看到 CMS 相关日志,说明 JVM 正在使用 Concurrent Mark Sweep(并发标记清除)收集器,常见于 JDK 8 及更早版本的老年代回收。它不追求吞吐量,而是尽量缩短停顿时间,适合对响应延迟敏感的系统。
关键阶段标识要认准
CMS 日志不是一行一条 GC,而是一次完整周期包含多个阶段,每阶段有固定前缀:
-
Initial mark:STW 阶段,快速扫描 GC Roots 直接引用的对象,日志里通常带
[GC (CMS Initial Mark)]或类似字样,耗时极短(毫秒级) -
Concurrent mark:并发执行,用户线程不停,日志中会出现
[CMS-concurrent-mark-start]和[CMS-concurrent-mark],持续时间长但不影响业务 -
Concurrent preclean:预清理,处理并发标记期间新产生的引用变化,日志含
[CMS-concurrent-preclean] -
Concurrent abortable preclean:可中断的预清理,为重新标记争取更干净的现场,日志里可能显示
[CMS-concurrent-abortable-preclean] -
Rescan (remark):第二次 STW,修正并发期间变动的对象,日志为
[GC (CMS Final Remark)],是 CMS 周期中最长的一次暂停 -
Concurrent sweep:并发清除死亡对象,日志含
[CMS-concurrent-sweep],不 STW -
Concurrent reset:重置 CMS 内部数据结构,准备下一轮,日志为
[CMS-concurrent-reset]
重点关注两类异常信号
CMS 本身不压缩内存,容易出问题,日志里出现这些内容要立刻警惕:
-
Concurrent Mode Failure:老年代还没来得及清理完就满了,被迫退化成 Serial Old 全堆 STW GC,日志会明确写
concurrent mode failure,这是严重性能事故征兆 -
Permanent Generation / Metaspace capacity exceeded(JDK 7/8):如果用了 CMS 但没配好元空间或永久代大小,常伴随
Full GC和PermGen space报错 -
Too many threads running concurrent phases:并发阶段耗时过长,比如
[CMS-concurrent-mark: X.XXX sec]超过几秒,说明堆太大、对象图太复杂或 CPU 不足
典型日志片段对照解读
例如这一段:
2026-09-15T14:22:03.102+0800: 12345.678: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2145678K(4194304K)] 2201345K(4718592K), 0.0042310 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2026-09-15T14:22:03.107+0800: 12345.683: [CMS-concurrent-mark-start]
2026-09-15T14:22:05.211+0800: 12347.787: [CMS-concurrent-mark: 2.104/2.104 secs] [Times: user=2.12 sys=0.01, real=2.10 secs]
2026-09-15T14:22:05.211+0800: 12347.787: [CMS-concurrent-preclean-start]
2026-09-15T14:22:05.234+0800: 12347.810: [CMS-concurrent-preclean: 0.023/0.023 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
2026-09-15T14:22:05.234+0800: 12347.810: [GC (CMS Final Remark) [YG occupancy: 55667K (524288K)]2026-09-15T14:22:05.234+0800: 12347.810: [Rescan (parallel) , 0.0123456 secs]2026-09-15T14:22:05.247+0800: 12347.823: [weak refs processing, 0.0001234 secs]... [Times: user=0.05 sys=0.00, real=0.01 secs]
→ 初始标记只用了 4ms;并发标记花了 2.1 秒;最终标记(remark)实际 STW 时间约 12ms(注意 real=0.01 secs 是墙钟时间);整个过程没有失败,属于健康运行。
别漏掉配套参数和上下文
CMS 日志必须结合启动参数看才有意义:
- 确认是否启用:
-XX:+UseConcMarkSweepGC(JDK 9+ 已废弃) - 老年代触发阈值:
-XX:CMSInitiatingOccupancyFraction=70表示老年代使用率达 70% 就启动 CMS 周期;若没设,默认约 92%,容易等不及就爆了 - 是否允许并发失败降级:
-XX:+UseCMSInitiatingOccupancyOnly可避免 JVM 自动调整阈值导致误判 - 配合新生代:
-XX:+UseParNewGC是 CMS 默认搭配的新生代收集器,日志里 ParNew 的频繁 Minor GC 会间接影响 CMS 触发频率










