CMS收集器旨在降低老年代GC停顿时间,通过并发标记清理减少STW,仅Initial Mark和Remark需停顿;关键在于合理设置启动阈值(建议70~80)、优化两阶段耗时、防止退化为Serial Old Full GC,并配合监控验证效果。

CMS 收集器的核心目标就是降低老年代 GC 的停顿时间,尤其适合对响应延迟敏感、CPU 资源较充足的 Java 应用(比如 Web 服务、实时交易系统)。它不能完全消除 STW,但能把大部分耗时工作放到与用户线程并发执行,真正需要停顿的只有 Initial Mark 和 Remark 两个阶段。要有效降低整体停顿,关键不是“加速单个 STW 阶段”,而是让 CMS 周期更稳定、更早启动、更少失败。
合理设置 CMS 启动阈值
老年代使用率一到临界点就触发 CMS 并发周期,这个阈值直接影响是否来得及完成标记清理。默认 -XX:CMSInitiatingOccupancyFraction=92 太高,等触发时老年代已非常紧张,容易在并发阶段中途因空间不足而失败,退化为 Serial Old 的 Full GC——这才是最致命的长停顿。
- 建议设为 70~80,例如 -XX:CMSInitiatingOccupancyFraction=75
- 搭配 -XX:+UseCMSInitiatingOccupancyOnly,避免 JVM 动态调整导致行为不可控
- 上线前通过压测观察老年代增长速率,再微调该值,目标是 CMS 周期总能在老年代用满前顺利完成
优化 Initial Mark 和 Remark 阶段耗时
这两个阶段必须 STW,但耗时可控。Initial Mark 主要扫描 GC Roots 和新生代中存活对象对老年代的引用;Remark 则需修正并发期间变化的引用关系。它们的负担直接受年轻代行为影响。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 启用并行处理:-XX:+CMSParallelInitialMarkEnabled 和 -XX:+CMSParallelRemarkEnabled(JDK 7u4+ 默认开启,显式配置更稳妥)
- 在 Remark 前主动做一次 Minor GC:-XX:+CMSScavengeBeforeRemark,减少新生代残留对象数量,从而缩小 Remark 扫描范围
- 控制对象晋升:调小 -Xmn、增大 -XX:SurvivorRatio、适当提高 -XX:MaxTenuringThreshold,避免短命对象过早进入老年代,减轻卡表(card table)压力
防止 CMS 退化和干扰
CMS 最怕的是“Concurrent Mode Failure”或被强制拉回串行模式,这会让所有优化归零。
立即学习“Java免费学习笔记(深入)”;
- 禁用显式 GC:-XX:+DisableExplicitGC,避免
System.gc()或 RMI 等框架触发 Full GC - 预留足够并发线程:-XX:ConcGCThreads=N(N 一般设为 CPU 核数的 1/4~1/2),确保并发标记/清理不因线程不足而超时
- 避免内存碎片影响分配:虽然 CMS 不整理内存,但可配置 -XX:+UseCMSCompactAtFullCollection 和 -XX:CMSFullGCsBeforeCompaction=1,在不得不 Full GC 时至少做一次压缩
必须配合监控验证效果
所有参数调优都要以真实日志为依据,不能凭经验猜测。
- 开启详细 GC 日志:-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log
- 重点关注日志中
[GC (CMS Initial Mark)和[GC (CMS Final Remark)的耗时,以及是否出现concurrent mode failure - 用工具如 GCViewer 或 GCEasy 分析日志趋势,确认 CMS 周期频率、各阶段耗时占比、失败次数是否下降

















