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