清理加剧内存碎片,只回收垃圾对象空间而不移动存活对象,导致空闲内存分散;整理消除碎片,将存活对象紧凑移至一端并清空边界外内存,但需stw、移动对象及更新引用。

清理(Sweeping)和整理(Compacting)是垃圾回收中两个关键但作用相反的阶段,它们对内存碎片的影响截然不同。
清理阶段会加剧内存碎片
清理阶段只做一件事:回收所有未被标记的“垃圾对象”所占的内存空间,但不移动任何存活对象。这导致堆内存中出现大量大小不一、位置分散的空闲块。
- 比如,原来有 A、B、C 三个存活对象依次排列,中间夹着几个已回收的小对象——清理后就留下 A □ B □□ C □ 这样的空洞分布
- 后续若需要分配一个较大对象(如 1MB 数组),即使总空闲内存足够,也可能因没有连续空间而失败,触发额外 GC 或扩容
- CMS 回收器就主要依赖标记-清除算法,因此长期运行后老年代容易出现碎片,最终可能引发 Full GC 或 Promotion Failure
整理阶段专门用于消除内存碎片
整理阶段把所有存活对象统一挪到堆内存的一端(例如起始地址),然后清空边界之外的所有区域。这样剩下的空闲空间就是一大块连续区域。
- 上例中 A □ B □□ C □ 经过整理后变成 A B C □□□□□,空闲区完全连通
- 对象移动后,所有指向它们的引用(栈中、其他对象字段中)必须同步更新,这是整理开销的主要来源
- Serial、Parallel、G1(部分区域)等收集器在老年代回收时都会执行整理,以保障大对象分配成功率
为什么不是所有 GC 都用整理?
整理虽能治碎片,但代价明显:必须暂停应用线程(STW)、移动对象、修正引用。所以设计上常做取舍:
- 新生代对象“朝生夕死”,复制算法(Copying)天然无碎片,比整理更轻量,因此不用 Compacting
- 老年代对象少但寿命长,复制成本高,于是标记-整理成为主流;CMS 为降低停顿放弃整理,靠预留空间+担保机制应对碎片
- G1 折中处理:以 Region 为单位局部整理,既控制 STW 时间,又逐步减少碎片










