CMS收集器的核心目标是获取最短回收停顿时间,通过初始标记、并发标记、重新标记、并发清除四阶段,仅两次极短STW,其余阶段与用户线程并发执行,采用标记-清除算法导致内存碎片。

CMS收集器的核心目标不是“快”,而是“不打断”——它把最耗时的标记和清除动作挪到用户线程运行的同时去做,只保留两次极短暂停(初始标记、重新标记),从而把老年代GC停顿压到毫秒级。这种设计适合B/S类对响应延迟敏感、CPU资源较充裕、对象生命周期相对稳定的系统。
四个关键阶段如何协同降低停顿
整个流程围绕“并发”展开,但并非全程无停顿:
- 初始标记:仅扫描GC Roots直接关联对象(如栈中引用、静态变量、JNI引用)+ 新生代中存活且引用老年代的对象。JDK 8起默认多线程执行,通常
- 并发标记:GC线程与用户线程交替执行(非严格并行),用三色标记法+增量更新机制持续追踪可达对象。用户请求照常处理,延迟略有上升,但不卡顿。
-
重新标记:修正并发期间因引用变更导致的漏标。需STW,但范围可控——主要扫描“脏卡页”(Card Table中标记为dirty的老年代区域)和新生代,而非全堆扫描。配合
-XX:+CMSScavengeBeforeRemark可提前触发YGC,减少其工作量。 - 并发清除:仅遍历标记位图,将未标记内存块归还空闲链表。不移动对象、不更新引用,因此完全无STW。本轮产生的新垃圾(浮动垃圾)留待下次处理。
并发带来的明确代价
低停顿不是免费的,CMS通过资源换时间:
-
CPU占用高:默认启用
(CPU核数 + 3) / 4个回收线程。在2核机器上占50% CPU,在4核机器上约25%,直接影响吞吐量。 -
内存碎片累积:“标记-清除”不整理空间,长期运行后老年代易出现大量不连续空闲块,大对象分配失败会触发
Concurrent Mode Failure,退化为Serial Old单线程Full GC,反而造成秒级停顿。 -
浮动垃圾无法处理:并发阶段用户线程持续分配,新产生的垃圾不在本轮清理范围内,需靠预留空间缓冲。若老年代使用率超阈值(默认92%,可通过
-XX:CMSInitiatingOccupancyFraction调低),就可能因空间不足而失败。
两种运行模式与典型风险
CMS实际有Background(常规并发)和Foreground(退化兜底)两种模式:
- Background模式是理想路径:含初始标记→并发标记→并发预处理→可中止预处理→重新标记→并发清除六步,其中预处理阶段主动扫描脏卡页、触发YGC等,都是为缩短重新标记STW时间。
- Foreground模式是故障路径:当并发失败发生(如老年代填满、浮动垃圾过多、CPU争抢严重),JVM会强制切换至Serial Old,全程STW,停顿不可控。这是CMS最需规避的场景。
- 常见诱因包括:
CMSInitiatingOccupancyFraction设得过高、年轻代过小导致频繁晋升、大对象直接进老年代、系统CPU长期饱和等。
适用边界与现实取舍
CMS不是万能低延迟方案,它只在特定条件下优势明显:
- 适合:Web服务端、API网关、交易中间件等强交互、容忍一定吞吐下降、有2核以上且负载可控的环境。
- 不适合:计算密集型批处理、嵌入式设备、单核或CPU常年90%+的系统;JDK 9起已废弃,JDK 14彻底移除,新项目应优先考虑G1或ZGC。
- 调优重点不在“加快GC”,而在“稳住并发”:合理设置触发比例、增大年轻代减缓晋升、监控
concurrent mode failure次数、避免大对象冲击老年代。


















