G1比CMS更少因内存碎片分配失败,因其将堆划分为固定大小Region,按收益回收,不依赖连续空间;而CMS老年代连续易碎片化,导致频繁失败和退化。
为什么G1比CMS更少因内存碎片分配失败
G1在设计上就避免了CMS那种“全堆并发标记+老年代碎片累积”的死循环。CMS的老年代是连续空间,大对象只能找连续空闲块;一旦碎片化,
或
就频繁触发,最终退化为Serial Old单线程GC——卡顿明显。G1把堆切成固定大小Region(默认1~32MB),回收时按收益排序选若干Region清理,天然不依赖连续空间。只要总空闲Region够用,哪怕分散,也能分配新对象。
但注意:G1不是完全免疫碎片。当大量中等对象(比如4–8MB)长期存活,又没被完整回收的Region塞满,就会出现“可用Region数量足、但每个都剩一点点空闲”的假性碎片——这时
仍可能触发Mixed GC,甚至退化到Full GC。
关键区别不在“有没有碎片”,而在“碎片是否阻断分配”:CMS一碎就卡,G1靠Region粒度和复制算法绕过
G1的
(默认45%)设太低,会过早启动Mixed GC,反而增加开销;设太高,则碎片积累到临界点才反应,风险上移
CMS下
能强制压缩,但只在Full GC时生效,而G1的
本身就是压缩式(mark-compact),无需额外开关
如何判断当前是G1碎片问题而非单纯内存不足
看GC日志里
后的动作:如果紧接着是
且耗时陡增(尤其
阶段超100ms),同时
(待回收Region数)远小于
(实际选入的Region数),说明G1在拼命凑Region却凑不够理想收益——这是碎片信号。而纯内存不足的表现是:每次Young GC后老年代占用持续攀升,
使用率很快逼近
容忍上限,但Mixed GC频率并不异常。
用
观察
(Mixed GC次数)和
(Concurrent Cycle次数)比值:若MGCC远高于CCSC,大概率是碎片驱动的被动回收
日志中搜索
——这是G1明确告诉你“想复制对象过去,但目标Region没整块空间了”,比OOM更早暴露碎片问题
CMS下对应现象是
后紧跟
括号标注,且后续GC日志出现
或
G1碎片优化的三个实操参数调整点
别迷信调大
。它只是目标,不是保证;设太高会让G1减少回收Region数,反而加剧碎片。真正该动的是这三项:
保持默认200ms或略降(如150ms),逼G1多选Region,提升空间利用率
根据对象大小分布调整:如果应用大量6MB对象,设为8MB能让单个Region刚好装下,减少跨Region引用和内部碎片;但设太大(如32MB)会导致小对象浪费空间,反而抬高碎片率
(默认10%)是预留缓冲区,防Mixed GC过程中新对象涌入导致失败;生产环境可提到15–20%,尤其写放大明显的场景(如频繁String拼接)
改完必须验证:
看
里
和
差值是否稳定,再配合
确认
频率是否回落。
CMS迁移到G1时最容易忽略的兼容陷阱
CMS可以和
搭配实现低延迟,但G1自己就是分代+区域混合模型,
在JDK9+已废弃,硬加会报错。更隐蔽的是JVM参数冲突:
对G1完全无效,但若配置残留,某些旧版监控脚本可能误读为“CMS启用”,导致告警逻辑错乱。
JDK8u40之后G1默认开启
(需
显式打开),CMS不支持;迁移后若发现
对象GC行为异常,先检查这个开关
CMS下
影响年轻代布局,G1里它被忽略——G1用动态计算的
(默认50%)管理Survivor空间,硬设
无意义
GC日志格式完全不同:CMS用
,G1必须加
(JDK10+)或
(JDK8)才能获取Region级信息
碎片问题从来不是单点参数能根治的,它暴露的是对象生命周期与GC策略的错配。G1给了更多腾挪空间,但也要求你更懂自己应用的对象尺寸分布和晋升节奏——日志里那行
,其实是JVM在提醒你:该看看对象到底怎么活下来的了。
Concurrent Mode Failurepromotion failureAllocation FailureInitiatingHeapOccupancyPercentUseCMSCompactAtFullCollectionFull GCAllocation FailureMixed GCOtherFree CSetTotal CSetOld GenMaxGCPauseMillisjstat -gc MGCCCCSCPrintGCDetailsto-space exhaustedconcurrent mode failure (concurrent mode failure)Abort PrecleanCMSCollector: abortMaxGCPauseMillisMaxGCPauseMillisG1HeapRegionSizeG1ReservePercentjmap -heap Heap Usageusedcapacityjstat -gc 1s MGCCParNew-XX:+UseParNewGC-XX:CMSInitiatingOccupancyFractionString Deduplication-XX:+UseStringDeduplicationStringSurvivorRatioTargetSurvivorRatioSurvivorRatio-XX:+PrintGCDetails -XX:+PrintGCTimeStamps-Xlog:gc*:file=:time,uptime,level,tags -XX:+PrintGCDetailsto-space exhausted