连载中 7/20

回收算法:清除、复制与整理的三难选择

2026-09-14 · 62 阅读 · 0 评论 · 0 赞

标记-清除:最直观,也最将就

标记阶段从 GC Roots 摸出活对象,清除阶段把没被摸到的直接抹掉——内存原地不动,简单粗暴。代价有两条:效率不稳定(对象越多标记越慢)和内存碎片(清完的空间东一块西一块)。碎片的恶果在分配大对象时爆发:空闲内存加起来明明够,却找不到一块连续空间,只能提前触发一次 Full GC。

复制算法:用空间换效率

// 复制算法三步走
// 1. 内存对半分成 A、B 两块,对象都先放 A
// 2. GC 时把 A 里的活对象整体搬到 B,按顺序排好
// 3. A 整块清空——无碎片,分配只需移动指针
//
// 新生代的改良版:Eden : Survivor0 : Survivor1 = 8 : 1 : 1
// 依据是"98% 的对象朝生夕死",不必对半浪费
// Minor GC 流程:Eden+S0 的活对象 → S1,年龄+1,然后 Eden+S0 清空

复制算法的账很好算:活对象越少越划算——只搬活的,垃圾一个都不碰。这正好契合新生代的特质(每次 GC 死 98%),所以新生代清一色用复制。万一 10% 的Survivor 装不下活对象,靠老年代做担保(Handle Promotion),装不下的直接进老年代。

标记-整理:老年代的务实之选

老年代恰恰相反:存活率高、对象顽强,复制算法要搬 90% 的对象,血亏。标记-整理应运而生:标记之后把活对象往内存一端压,边界外的整块清理。无碎片,但搬家有代价——所有被移动对象的引用都要更新,这一步通常是 STW 的。清除、复制、整理,正好是三种取舍的三角。

分代收集:按寿命各就各位

算法优点缺点用在哪
标记-清除不移动对象,实现简单碎片、效率不稳老年代(CMS)
复制无碎片、活对象少时极快浪费空间、存活高时慢新生代
标记-整理无碎片、空间利用率高移动对象有停顿老年代(Parallel Old、G1)

分代之下还有个绕不开的问题:跨代引用——老年代对象引用新生代对象,Minor GC 却只想扫新生代。全堆扫描代价太大,于是有了记忆集/卡表:老年代被划成一张张卡,谁引用了新生代就记谁的卡,Minor GC 只扫这几张卡,用小空间记录换大范围的扫描。

三种 GC 的名字先理清

Minor GC:只收新生代,频繁、快;Major GC:常被用来指收老年代,但这个词没有统一标准;Full GC:整堆回收,最慢、最伤、要尽量避免。看到监控里 Full GC 频繁,先别急着调参,下一篇看看执行这些回收的具体选手——各路收集器。

503

10 年全栈工程师 · 503咖啡馆主理人

#垃圾回收算法#标记清除#复制算法#标记整理#分代收集

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 1 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞