cms被标记为过时,根本原因在于其设计矛盾:低停顿牺牲稳定性、可控性与维护成本;g1则从内存模型(region化)、算法逻辑(标记-整理)和工程落地(目标驱动调优)三层面重构gc范式。

CMS被标记为过时,根本原因在于它在实际生产中暴露了难以调和的设计矛盾:追求低停顿却牺牲稳定性、可控性与维护成本;而G1不是简单“更好一点”,而是从内存模型、算法逻辑和工程落地三个层面重构了GC的设计范式。
内存布局:连续大块 vs 灵活Region
CMS沿用传统分代模型,年轻代和老年代各自占据一大片连续内存。这种结构导致两个硬伤:一是老年代碎片化后无法分配大对象,频繁触发Full GC;二是扩容缩容僵硬,无法按需调度资源。
G1把整个堆切分为2048个左右大小相等的Region(默认1–32MB),每个Region可动态扮演Eden、Survivor、Old甚至Humongous角色。这种“化整为零”带来三重好处:
- 回收时只挑垃圾多、耗时短的Region组合,实现真正意义上的局部收集
- 跨Region复制存活对象天然完成内存整理,彻底规避碎片问题
- 大对象直接落入Humongous区,避免在普通Old区反复搬运或触发压缩
回收算法:标记-清除 vs 标记-整理
CMS用标记-清除算法实现并发清理,不移动对象——这是它能减少STW的关键,也是所有问题的源头:
- 长期运行后老年代布满碎块,一旦需要分配稍大对象(比如缓存批量加载、JSON解析),立即触发Concurrent Mode Failure,回退到Serial Old,停顿飙升至秒级
- 虽提供-XX:+UseCMSCompactAtFullCollection,但压缩过程本身就要长时间STW,违背低延迟初衷
- 浮动垃圾无缓冲机制,必须预留空间(默认68%触发),调参稍激进就失败,运维成本极高
G1全程采用标记-整理(本质是复制+整理),每次YGC或Mixed GC都自动腾挪、合并空间。不需要额外压缩阶段,也不依赖人工干预,碎片控制从“事后补救”变成“过程内生”。
可控性与工程实践:参数黑洞 vs 目标驱动
CMS的调优像解谜游戏:要平衡-XX:CMSInitiatingOccupancyFraction、-XX:CMSMaxAbortablePrecleanTime、并发线程数、Preclean次数……稍有不慎就出现“越调越卡”。很多团队最终发现:不调反而最稳。
G1只用一个核心目标参数就能引导整个回收行为:
- -XX:MaxGCPauseMillis=200(默认值),G1会自动调整Region选取数量、年轻代大小、混合回收频率,尽力满足该目标
- 配合-XX:+UseG1GC开箱即用,JDK 9起默认启用,无需额外配置即可稳定运行
- 监控更直观:通过G1EvacuationPause、G1MixedGC等日志可清晰看出回收节奏是否健康
现实表现:故障率与替代路径已成定局
真实生产数据表明:在8GB以上堆、多核服务器场景下,G1的Full GC频次比CMS低一个数量级,平均STW时间波动小、长尾可控。更重要的是,它让GC从“黑盒调参”回归到“白盒治理”——你不再需要背熟二十个参数,只需关注应用实际延迟需求。
Oracle在JDK 9废弃CMS,JDK 14彻底移除,不是技术迭代的偶然,而是对“可用性优先”原则的确认。后续ZGC、Shenandoah的演进,也都继承了G1确立的范式:基于Region、目标驱动、免调优优先。CMS的落幕,标志着Java GC正式告别“靠人堆参数”的时代。











