cms收集器通过并发回收降低老年代stw停顿,需搭配parnew、固定新生代大小、保守设置启动阈值(如65%)、强制预清理minor gc、多线程remark及定期压缩来防控失败与碎片。

要减少 Java 应用的 GC 停顿,CMS(Concurrent Mark-Sweep)收集器是 JDK 8 及之前版本中针对老年代低停顿的关键方案。它不追求高吞吐,而是把大部分回收工作放到与业务线程并发执行,让 STW(Stop-The-World)时间稳定在毫秒级。但 CMS 不是开箱即用的,必须配合新生代策略、阈值控制和日志验证才能真正压低停顿。
启用 CMS 并绑定 ParNew 新生代收集器
CMS 只负责老年代,必须搭配 ParNew 才能构成完整低停顿组合:
- -XX:+UseConcMarkSweepGC:显式启用 CMS(JDK 9+ 已废弃,仅限 JDK 7/8)
- -XX:+UseParNewGC:强制启用 ParNew 作为新生代收集器(CMS 默认要求,但显式声明更可靠)
- -Xmn2048M 或 -XX:NewSize=2048M -XX:MaxNewSize=2048M:必须手动固定年轻代大小;避免依赖 NewRatio,否则晋升节奏不可控
精准控制 CMS 启动时机,防 Concurrent Mode Failure
停顿飙升的主因常是 CMS 没来得及启动,老年代就满了,被迫退化为 Serial Old 全停顿 Full GC。关键靠预留空间:
- -XX:CMSInitiatingOccupancyFraction=65:设为 65(即老年代使用率达 65% 时触发),比默认 68%–92% 更保守,为浮动垃圾留出缓冲
- -XX:+UseCMSInitiatingOccupancyOnly:禁用 JVM 动态调整,确保每次严格按设定值触发,行为可预测
- 监控 GC 日志中的 concurrent mode failure 或 promotion failed,一旦出现,说明阈值已失守,需进一步下调至 60 或增大老年代
压缩 Remark 阶段 STW 时间
Remark 是 CMS 中最易超时的 STW 环节,常达百毫秒以上。优化重点是减少跨代引用扫描量:
- -XX:+CMSScavengeBeforeRemark:Remark 前强制一次 Minor GC,清理掉大量即将死亡的年轻代对象,避免它们晋升后增加 remark 负担
- -XX:+CMSParallelRemarkEnabled:启用多线程 remark,显著降低单核压力
- -XX:+CMSParallelInitialMarkEnabled:加速 Initial Mark(对大堆效果明显)
缓解浮动垃圾与内存碎片影响
浮动垃圾无法在本轮回收,叠加碎片会加剧分配失败风险:
- 通过 -XX:CMSMaxAbortablePrecleanTime=5000 控制预清理耗时,避免等待过久错过最佳回收窗口
- 设置 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=1,让每次 Full GC 后自动整理内存,延缓碎片恶化
- 生产环境建议老年代预留 30%–40% 空间作浮动垃圾缓冲,例如总堆 4G,老年代设为 2.8G,而非硬占满
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











