cms采用标记-清除算法,不清除后不移动存活对象,导致老年代内存碎片化;g1通过region划分与复制式回收,实现局部、增量的空间整合,避免大对象分配失败。

CMS 使用标记-清除算法,不移动对象,只标记存活对象、清除未标记部分。这会导致老年代中出现大量不连续的空闲内存块——也就是物理碎片。当需要分配大对象时,即使总空闲空间足够,也可能因找不到连续区域而触发 Full GC。
CMS 的“标记-清除”怎么造成碎片
它分四步:初始标记(STW)、并发标记、重新标记(STW)、并发清除。关键在最后一步——清除只是把垃圾对象占用的空间“标为空闲”,但不会挪动任何存活对象。久而久之,存活对象像岛屿一样散落在堆里,中间夹着零散缝隙。
- 比如老年代有 1GB,已用 700MB,但分散成 200 多个 1–4KB 的小块,此时一个 512KB 的新对象就无法分配
- 这种问题在长期运行、频繁创建中等大小对象的服务(如订单缓存)中特别明显
- CMS 本身不处理碎片,只能靠 -XX:+UseCMSCompactAtFullCollection 这类参数,在 Full GC 时临时压缩,但代价是更长停顿
G1 的“物理碎片化压缩”不是实时整理,而是按 Region 拆分+选择性复制
G1 把整个堆划分为 2048 个固定大小的 Region(比如每块 1MB),新生代和老年代不再固定分区,而是由 Region 动态组成。回收时,G1 优先挑“垃圾最多”的 Region,把其中的存活对象复制到其他空 Region 中,再整块释放源 Region。
- 复制过程天然完成整理:目标 Region 中的存活对象是紧密排列的,没有空洞
- 被回收的 Region 变成完全干净的整块空间,后续分配大对象时可直接使用
- 这种压缩是局部的、增量的,不等堆快满才动手,所以避免了 CMS 那种“突然发现没法分配、紧急 Full GC”的窘境
关键区别不在“是否压缩”,而在“何时压缩、怎么压缩”
CMS 是“不压缩→等碎片爆了→被迫全堆压缩”;G1 是“边回收边压缩→每次只动几个 Region→压缩结果立即可用”。前者追求单次停顿最短,后者追求长期稳定可控。
如果应用堆超过 4GB、停顿要求控制在 200ms 内且不能接受偶尔秒级卡顿,G1 的 Region 管理和复制式压缩机制就比 CMS 更可靠。











