cms收集器的核心目标是降低老年代gc停顿时间,通过将标记-清除拆解为初始标记(stw)、并发标记、并发预清理、重新标记(stw)和并发清除五个阶段实现并发执行;其三大缺点是内存碎片化、并发模式失败和浮动垃圾与cpu资源竞争,需针对性调优或迁移到g1/zgc。

CMS收集器的核心目标是降低老年代GC停顿时间,它通过把原本需要全程STW的标记-清除过程拆解为多个可并发执行的阶段来实现。但这个设计也带来了几个关键问题,理解流程本身,就是理解如何应对这些缺点的起点。
CMS的五阶段工作流程
CMS实际运行包含五个逻辑阶段(部分资料合并预清理与重新标记),并非简单的四步:
- 初始标记(Initial Mark):STW,极短。仅标记GC Roots直接可达对象,以及年轻代中存活对象所引用的老年代对象。
- 并发标记(Concurrent Mark):与用户线程并行。从初始标记结果出发,遍历整个老年代对象图。期间应用线程修改引用会产生“脏卡”,由写屏障记录。
- 并发预清理(Concurrent Preclean):继续并发执行。扫描上一阶段标记为“脏”的内存页(Card),处理并发标记后期新产生的引用变更,减少后续STW压力。
- 重新标记(Remark):STW,时长取决于年轻代大小和“脏卡”数量。修正并发过程中遗漏的引用关系,确保标记准确性——这是保证回收安全的关键环节。
- 并发清除(Concurrent Sweep):并发执行。回收所有未被标记的对象空间,不移动存活对象,仅更新空闲链表。此时新产生的垃圾称为“浮动垃圾”,留待下次GC。
CMS三大典型缺点及对应缓解思路
缺点不是故障,而是设计权衡的体现;针对性调优才能让CMS在合适场景下稳定运行:
- 内存碎片化:因采用标记-清除且不整理内存,长期运行后空闲空间分散。大对象分配易触发 Concurrent Mode Failure,进而退化为 Full GC。缓解方式包括:提前触发CMS(调低 -XX:CMSInitiatingOccupancyFraction)、启用压缩(-XX:+UseCMSCompactAtFullCollection)、或搭配使用 ParNew 新生代收集器减少晋升压力。
- 并发模式失败(Concurrent Mode Failure):老年代在并发清理完成前就填满,被迫启动 Full GC。常见于老年代增长过快或 CMS 启动太晚。可通过 监控 CMS 触发时机与耗时、增大老年代容量、或 调整触发阈值与后台线程数(-XX:ConcGCThreads) 来规避。
- 浮动垃圾与CPU资源竞争:并发阶段占用额外线程,挤占应用吞吐量;同时无法清理运行中产生的新垃圾。需权衡响应延迟与系统整体负载,避免在高CPU争抢场景强行启用CMS,必要时考虑升级到G1或ZGC。
适用边界与现实取舍
CMS适用于对单次停顿极度敏感、老年代对象存活率不高、且硬件资源(尤其是CPU)相对充裕的服务端应用,例如早期电商交易链路或实时行情推送。但它不适用于堆内存极大(>8GB)、对象生命周期长、或要求高吞吐的批处理场景。JDK 9起官方已弃用CMS,JDK 14彻底移除,其思想遗产(如并发标记、增量更新)则被G1、ZGC继承演进。当前生产环境若仍在用CMS,重点不在“怎么修”,而在于“何时迁”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











