cms没有增量收集模式,其本质是并发标记-清除机制,核心目标是降低stw时间;实际需调控跨代引用处理、浮动垃圾控制和remark开销抑制三项关键权衡。

明确CMS中“变量回收”的实际含义
Java中变量本身不被GC直接回收,真正被回收的是**堆上不再可达的对象实例**。CMS对“变量所引用对象”的回收决策,取决于该对象是否在并发标记过程中被正确标记为存活。而能否正确标记,直接受制于三个现实约束:
- 跨代引用干扰:新生代局部变量(如方法栈中的localObj)可能临时引用老年代对象,若未及时追踪,会导致漏标;
- 并发期间引用变更:Remark前用户线程仍在修改引用关系,造成“标记滞后”,需重新扫描变动部分;
- 浮动垃圾积累:并发清除阶段新产生的死亡对象无法本次回收,占用空间并推高老年代占用率,间接影响下一次CMS触发时机。
实战中必须调控的三项关键权衡
这些不是理论选项,而是上线后必须观测、调整的具体参数与策略:
- 用CMSScavengeBeforeRemark压制Remark耗时:强制在重新标记前执行一次Minor GC,大幅减少需扫描的新生代对象数量。某支付系统启用后,200ms+ STW从每小时5次降至零。但代价是增加一次Minor GC频率,需确认Young GC停顿可接受(通常
- 靠CMSInitiatingOccupancyFraction预留浮动垃圾空间:设为70(即老年代达70%触发CMS),而非默认92。留出30%空间容纳并发阶段新分配+浮动垃圾。过低(如50)会频繁触发CMS;过高(如85)易引发Concurrent Mode Failure,退化为Full GC;
- 通过CMSScheduleRemarkEdenPenetration+CMSScheduleRemarkEdenSizeThreshold引导预清理时机:例如设EdenPenetration=50%、SizeThreshold=2MB,让CMS在Eden使用超2MB且达50%时启动可中断预清理,主动“消化”跨代引用变更,避免全量扫描新生代。这是降低Remark延迟最精细的杠杆。
监控与验证变量回收效果的关键指标
不能只看GC日志里的“[CMS-concurrent-abortable-preclean]”,要关联分析以下三组数据:
- 晋升速率(Promotion Rate):单位时间从新生代晋升至老年代的对象大小(MB/s)。若持续高于CMS回收能力,必然导致并发失败;
- Remark耗时分布:重点关注95分位值。若>100ms,优先检查是否缺失CMSScavengeBeforeRemark或Eden过大;
- 老年代碎片率:通过jstat -gc输出的CMS触发前后老年代可用空间(CCS)与总容量比值估算。碎片率>30%时,大对象分配易失败,需考虑UseCMSCompactAtFullCollection或计划重启。











