发布于2026-07-08 阅读(0)
扫一扫,手机访问
说到CMS和G1在碎片处理上的区别,很多人的第一反应是“CMS碎片多,G1碎片少”。这话对,但没说到根上。两者真正的分野,不在于“会不会产生碎片”,而在于一个更关键的问题:碎片是不是会立刻堵住内存分配的通道。CMS因为算法和内存布局的设计,碎片一旦出现,分配就可能直接失败;G1靠分区和整理机制,把碎片困在局部区域,不让它挡全局的路。

CMS底层用的是标记-清除算法——只回收垃圾,不挪动活对象。老年代是一整块连续的地址空间,回收之后空闲内存变成零零散散的小块。这时候如果你要分配一个比较大的对象,比如数组或者缓存块,就必须找到一块连续的空闲区域。一旦找不到,哪怕总空闲空间还有不少,也会直接触发 promotion failure 或 concurrent mode failure,然后退化成Serial Old全堆压缩GC——代价极高,停顿很长。
G1走的是另一条路:标记-整理(其实是局部复制)。在筛选回收阶段,它把选中的Region里的存活对象复制到别的Region,原Region直接整体释放。这个动作本身就是空间整理——用完的Region干干净净,不会有“明明有空位却塞不进”的假性耗尽问题。
CMS的老年代是单一连续的物理段,碎片随着运行时间越积越多,而且没法隔离——碎成渣了也得硬扛。而G1把堆切成固定大小的Region(默认1–32MB),每个Region之间是独立的。分配对象时,只要系统里还有一个空闲Region(或者多个Region拼在一起能满足需求),就能完成分配。它不依赖一段连续的虚拟地址空间。碎片被限制在单个Region内部,不影响跨Region的分配。哪怕某些Region剩一点点空间,只要整体上空闲Region数量够,分配就不会停。
CMS日常对碎片几乎没什么作为。只有等到Concurrent Mode Failure或者Full GC时,才可能借助UseCMSCompactAtFullCollection这个开关触发一次压缩——但这是高停顿的补救措施,而且只出现在Full GC场景。说白了,平时不管理,等到崩了才收拾。
G1的Mixed GC本身就是整理过程:它按照垃圾密度和回收收益动态选择要回收的Region,回收的同时完成复制和腾空。也就是说,碎片控制是常态化的、渐进式的——不是等捅了篓子再压,而是一边用一边理,细水长流。
从日志上看,两者的失败信号也很不一样:
看懂这些信号,才能在实际调优时对症下药。而不是看到“碎片”两个字就盲目切换GC。