垃圾回收算法

§2.1.2 垃圾回收算法

考察意图:理解各算法的原理、优缺点和适用分代,能结合业务场景说明选型理由。

回答样板

四种基础算法,JVM根据分代特性组合使用:

1. 标记-清除(Mark-Sweep)——最基础

  • 流程:标记阶段遍历GC Roots标记存活对象 → 清除阶段回收未标记对象
  • 优点:算法简单,不移动对象
  • 缺点:内存碎片化——清除后产生大量不连续的空闲内存。碎片严重时,即使总空闲内存足够,也可能无法分配大对象,触发Full GC
  • 适用:老年代的基础算法(CMS的标记-清除阶段)

2. 标记-复制(Mark-Copy)——新生代首选

  • 流程:将存活对象复制到另一块空白区域 → 原区域整块清空
  • 优点:无内存碎片,整块清空效率极高,只需移动存活对象
  • 缺点:浪费50%空间(需要一块空白区域做副本)。不过新生代对象98%朝生夕死,实际只需要很小的Survivor空间
  • 适用:新生代Minor GC。8:1:1的Eden:Survivor比例就是基于这个假设——Survivor只需要留新生代10%的空间。如果Survivor不够放存活对象,通过分配担保机制直接晋升老年代

3. 标记-整理(Mark-Compact)——老年代兜底

  • 流程:标记存活对象 → 将所有存活对象移到内存一端 → 清理边界外的内存
  • 优点:无内存碎片,不需要浪费额外空间
  • 缺点:移动对象需要STW,且需要更新所有指向被移动对象的引用——停顿时间与堆中存活对象数量成正比
  • 适用:老年代(Serial Old、Parallel Old)

4. 分代收集(Generational Collection)——JVM的顶层策略

  • 不是独立算法,而是"不同分代用不同算法"的策略组合
  • 核心假设:弱分代假说——绝大多数对象朝生夕死(新生代用复制算法);强分代假说——熬过多次GC的对象一般不会轻易死亡(老年代用标记-清除/标记-整理)
  • 几乎所有JVM垃圾回收器都基于分代假设设计

记忆集(Remembered Set)与卡表(Card Table)

分代收集的致命问题:跨代引用。新生代对象被老年代引用时,Minor GC只扫新生代,无法通过GC Roots找到这个老年代引用,导致存活新生代对象被误清。解决方案:老年代维护一张卡表——将老年代内存按512字节分块(Card),有老年代引用指向新生代时,对应Card标记为"脏"。Minor GC扫描时除了GC Roots还扫描卡表中的脏Card,避免全堆扫描。CMS和G1都在写屏障(Write Barrier)中维护这张卡表,性能开销约5%-10%。

陷阱提示:四种算法的优缺点和应用分代背串;不知道跨代引用问题和卡表机制;以为复制算法就是新生代的全部。

本作品采用 CC BY-NC-SA 4.0 协议进行许可
使用 Hugo 构建
主题 StackJimmy 设计