CMS垃圾收集器是Java虚拟机中一款以低停顿为目标的并发标记清除收集器,适合JDK 8时代对响应时间敏感的Web应用,但因其内存碎片和并发失败等缺陷,在JDK 9后被标记为废弃,JDK 14正式移除。 如果你维护老系统,或者正准备Java面试,理解CMS的运作机制和取舍逻辑依然很有价值。
CMS垃圾收集器的工作原理是什么?
CMS全称Concurrent Mark Sweep,核心目标是把Full GC的停顿时间压到最短,它基于标记-清除算法,整个收集周期被拆成多个阶段,只有少数阶段需要暂停用户线程。
初始标记:短暂STW,只找直接关联
这个阶段会暂停所有用户线程,也就是Stop The World,它只标记GC Roots能直接关联到的对象,不继续向下遍历,因为范围小,停顿通常很短,多数情况下在几毫秒到几十毫秒之间。
并发标记:与用户线程一起跑
从初始标记的对象出发,沿着引用链遍历整个对象图,这个阶段耗时最长,但和用户线程并发执行,不会造成明显停顿,并发标记期间,对象引用可能发生变化,所以需要后续阶段修正。
重新标记:修正并发期间的变动
这个阶段再次STW,处理并发标记期间产生的新增引用和漏标对象,CMS采用增量更新方式,重新标记的停顿比初始标记长,但远短于传统Full GC,可以通过-XX:+CMSParallelRemarkEnabled开启并行重新标记来缩短停顿。
并发清除:清理垃圾,与用户线程并发
重新标记完成后,CMS进入并发清除阶段,直接清理未被标记的对象,这个阶段不移动对象,所以不会产生内存整理动作,停顿几乎为零,但正因为不移动对象,内存碎片问题就此埋下。
并发重置:为下一次收集做准备
清除完成后,CMS重置内部数据结构,等待下一次触发,整个周期可以用下表概括:
| 阶段 | 是否STW | 主要动作 | 停顿特点 |
|---|---|---|---|
| 初始标记 | 是 | 标记GC Roots直接关联对象 | 很短 |
| 并发标记 | 否 | 遍历对象图 | 无停顿 |
| 重新标记 | 是 | 修正并发标记变动 | 较短 |
| 并发清除 | 否 | 清理未标记对象 | 无停顿 |
| 并发重置 | 否 | 重置状态 | 无停顿 |
CMS垃圾收集器的优缺点有哪些?适合哪些场景?
CMS的优缺点非常鲜明,选型时不能只看低停顿一个指标。
优点:低停顿是最大卖点
- 停顿时间短:并发标记和并发清除把主要工作放在用户线程运行期间,适合对响应时间敏感的在线服务。
- 并发收集:多数阶段不暂停应用,电商大促、在线游戏、实时API等场景能从中受益。
- 成熟稳定:在JDK 8时代经过大量生产验证,调优资料丰富,社区经验多。
缺点:碎片和并发失败是硬伤
- 内存碎片:标记-清除不整理内存,长期运行后大对象可能找不到连续空间,触发Full GC。
- CPU资源敏感:并发阶段会占用CPU,核心数少的机器上应用吞吐量会下降。
- 浮动垃圾:并发清除时用户线程还在产生新垃圾,只能留到下次收集。
- 并发失败:如果老年代在并发清除完成前被填满,会退化成Serial Old收集器,造成长时间STW。
- 无法处理超大堆:堆越大,并发标记时间越长,失败概率越高。
适合与不适合的场景
适合的场景包括:JDK 8上的Web应用、堆内存中等偏大、要求单次停顿低于几百毫秒、CPU核心数较多,比如电商大促场景下CMS调优,重点就是控制老年代增长速度和并发失败。
不适合的场景包括:大数据批处理、超大堆内存、吞吐量优先的后台任务、JDK 11以上新项目,这些场景更适合G1、ZGC或Shenandoah。
CMS和G1垃圾收集器哪个好?对比与选型建议
这是面试和线上选型绕不开的问题,两者设计目标不同,直接对比才有意义。
核心差异对比
| 对比项 | CMS | G1 |
|---|---|---|
| 内存布局 | 分代连续 | 分区Region |
| 停顿控制 | 尽力而为 | 可预测停顿模型 |
| 碎片处理 | 不整理,需Full GC压缩 | 整体复制,天然整理 |
| 适用堆大小 | 中等堆,通常小于8GB | 大堆,可超过16GB |
| JDK支持 | JDK 9废弃,JDK 14移除 | JDK 9后默认 |
| 调优复杂度 | 参数多,阈值难控 | 相对简单,目标驱动 |
选型建议
- 堆小于8GB、JDK 8、团队熟悉CMS参数,可以继续用CMS。
- 堆大于8GB、要求可预测停顿、JDK 11及以上,优先选G1。
- 追求极低停顿且堆很大,考虑ZGC或Shenandoah。
- 新项目不建议再引入CMS,行业共识认为它已完成历史使命。
调优参数实战
如果老系统仍在使用CMS,以下命令可以直接参考:
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=70
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=0
-XX:+CMSScavengeBeforeRemark
CMSInitiatingOccupancyFraction控制老年代使用率达到多少触发CMS,设得太高容易并发失败,设得太低则GC频繁,通常从70开始观察,再根据GC日志微调。
上海Java面试常问CMS问题及调优实操
上海Java后端岗位对JVM的考察比较细,CMS相关题目出现频率不低,以下问题值得提前准备。
面试高频问题
- CMS收集过程分哪几个阶段?哪些阶段STW?
- 什么是浮动垃圾?为什么CMS会产生浮动垃圾?
- Concurrent Mode Failure怎么产生?如何避免?
- CMS和G1在内存布局和停顿控制上的区别是什么?
- CMS为什么被废弃?
调优步骤与命令
- 开启GC日志:
-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC - 观察Full GC频率和CMS GC耗时,重点看
concurrent mode failure字样。 - 调整触发阈值:
-XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly - 开启碎片整理:
-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 - 监控CPU使用率,并发阶段CPU飙升属于正常现象,但持续过高会影响吞吐。
- 如果碎片严重且无法避免,考虑升级到G1。
不少企业购买JVM调优服务价格从几千到数万元不等,但掌握上述命令和日志分析方法,多数CMS问题可以自行定位,业内专家指出,理解收集器阶段比死记参数更重要。

CMS垃圾收集器在JDK版本中的演进与替代方案
据Oracle官方文档描述,CMS在JDK 5引入,JDK 9被标记为废弃,JDK 14正式移除,这个时间线说明它已经不适合新项目。
替代方案主要有三个:
- G1:JDK 9后的默认收集器,分区管理,可预测停顿,适合大多数服务端应用。
- ZGC:JDK 11引入实验特性,JDK 15转正,停顿控制在极低水平,适合超大堆。
- Shenandoah:OpenJDK社区推动的低停顿收集器,与ZGC目标类似。
老系统如果还在用CMS,建议先评估升级JDK的收益和风险,如果暂时无法升级,至少要把CMSInitiatingOccupancyFraction和碎片整理参数配置合理,减少并发失败概率。
CMS是低停顿收集器发展史上的重要一步,但它不整理内存、CPU敏感、并发失败等缺陷决定了它终将被替代,理解它的原理和调优方法,既能应对老系统维护,也能在面试中展现扎实的JVM功底。
Q&A:Java虚拟机CMS垃圾收集器常见疑问解答
CMS垃圾收集器为什么被废弃?
CMS被废弃的核心原因是内存碎片和并发失败难以根治,它采用标记-清除算法,不移动对象,长期运行后大对象分配容易失败,并发清除期间用户线程继续产生垃圾,一旦老年代提前填满,就会退化成Serial Old,造成长时间停顿,JDK 9标记废弃,JDK 14移除,官方推荐迁移到G1或ZGC。
CMS的并发失败怎么解决?
并发失败通常是因为老年代增长快于CMS清理速度,可以尝试三个方向:降低CMSInitiatingOccupancyFraction让CMS更早启动;增加堆内存或年轻代大小,减缓对象晋升速度;开启-XX:+UseCMSCompactAtFullCollection和-XX:CMSFullGCsBeforeCompaction=0,在Full GC时压缩内存,如果调整后仍频繁失败,说明CMS已不适合当前负载,应升级到G1。
CMS和Parallel GC哪个更适合电商场景?
电商场景通常对响应时间敏感,CMS的低停顿更有优势,Parallel GC关注吞吐量,Full GC停顿时长可能达到秒级,容易导致请求超时,但CMS的并发阶段会占用CPU,如果服务器核心数少,反而可能拖慢整体处理能力,行业共识认为,核心数充足且堆内存在8GB以内时,CMS适合电商在线服务;如果追求更高吞吐或堆更大,G1是更平衡的选择。

