面试记录 2026-09-21
题目:JVM有哪些垃圾回收算法?
难度:medium
标签:jvm/gc, general/knowledge, difficulty/medium
评分:⭐⭐⭐⭐☆(4/5)
我的回答
JVM 的垃圾回收算法核心其实是分代分代思想,然后划分了年轻代、老年代,然后去进行数据的回收、对象的回收、内存的对象的回收。核心其实是这个,然后又是在这个分代思想上建立了不同的分代、不同的处理方式,也就是垃圾回收算法。不同代的垃圾回收算法使用的常规是不太相同的。针对年轻代,朝生夕死这种对象,存活量不多,使用的常见算法是复制、删除或者是复制整理。它把整个对象复制,有还存活的复制到另一个区域,没存活的直接就回收了。然后这是一常规的算法。另外就是复制标记清理,不做整理就直接清理,只标记,然后清理。这时候可能会产生一些内存碎片,这种常用在常规的老年代,因为老年代的对象你不好去对它进行整体的复制,也没有空间去存,相对占用的空间位置较大,没有地方再去存它,存活的时间也比较久的一种对象。再有是后续的多线程的算法,但实际上本质它还是标记清除跟标记整理两个的分类。组成的。然后后续的话,像 G 一里边使用的算法其实是由复制算法演变的,也就是标记整理。它把不同的 Reason 的对象相互复制,然后去清理对应的 Region。大概是这个样子,我理解的垃圾回收算法。
点评
优点:
- 正确区分新生代用复制、老年代用标记-清除/标记-整理,这是 GC 算法题的核心分代假设应用
- 准确说出标记-清除的内存碎片缺陷,以及为什么老年代不适合复制(存活对象多、空间不足)
- 能主动把 G1 的 Region 复制和标记-整理对应起来,理解到"G1 是复制算法的演变"
- 认识到"多线程只是执行方式,本质还是这两种",概念清晰
不足:
- 基础算法术语不准:三种经典算法应是标记-清除、复制、标记-整理,不是"复制、删除、复制整理";"复制标记清理"术语混合混乱
- 缺少分代假设的名称:没提到"弱分代假说(几乎所有对象朝生夕死)"这个术语,面试官想听的关键词
- G1 描述有偏差:G1 是"标记-整理 + Region 间复制"的混合,Region 内不整理(保留碎片避免移动开销),跨 Region 才复制;不是纯"标记整理"
- 漏了 ZGC/Shenandoah 的定位:并发标记-整理+卡表(Card Table)+读屏障(Load Barrier),是 GC 算法的第四代演进,属于当前面试加分点
- 没提"整理"的具体含义:整理 = 存活对象压缩到一侧,避免碎片;移动对象需要处理所有引用(写屏障)
补充:
- 三种经典算法对比:
- 标记-清除(Mark-Sweep):简单但有碎片问题
- 复制(Copy):无碎片但浪费一半空间(新生代 Eden:From:To = 8:1:1)
- 标记-整理(Mark-Compact):无碎片但要移动对象,需要处理引用(写屏障)
- 现实 GC 器都是组合:
- CMS = 新生代复制 + 老年代并发标记-清除(有碎片)
- ParallelGC = 新生代复制 + 老年代并行标记-整理(吞吐优先)
- G1 = Region 复制 + 标记-整理,可预测停顿
- ZGC / Shenandoah = 并发标记-整理 + 颜色指针/读屏障(<10ms 停顿)
- 写屏障(Write Barrier) 是移动/并发引用的关键技术,配合三色标记、卡表使用
完整讲解
第一部分:知识重点剖析
核心考点定位:考察三大经典 GC 算法的机制与权衡,以及现实 GC 器如何组合演化。
知识点拆解:
- 标记-清除(Mark-Sweep)
- 流程:从 GC Roots 标记存活 → 统一清理未标记
- 优点:实现简单、无需移动对象
- 缺点:内存碎片,长时间运行后大对象无法分配
- 使用者:CMS 老年代、ZGC 早期阶段
- 复制(Copy)
- 流程:堆分两半(Eden + Survivor),GC 时把存活复制到另一半,清空旧半
- 优点:无碎片、存活少时效率极高
- 缺点:浪费空间(存活多时开销大),所以只适合新生代
- HotSpot 优化:Eden:From:To = 8:1:1,Survivor 从 512MB 降到 16MB,让老年代吃满剩余空间
- 使用者:所有分代 GC 的新生代(Serial、ParNew、Parallel、G1、ZGC)
- 标记-整理(Mark-Compact)
- 流程:标记存活 → 把存活对象压缩到一侧,避免碎片
- 优点:无碎片、空间利用率高
- 缺点:移动对象要处理所有引用(写屏障 / TLAB 重建),STW 时间随老年代增长
- 使用者:ParallelGC、G1(Region 间)、ZGC、Shenandoah
- GC 器 = 算法组合 + 执行方式
| GC 器 | 新生代 | 老年代 | 停顿特征 |
|---|
| Serial | 复制 | 标记-整理 | 单线程,短停顿 |
| ParallelGC(默认 JDK 8) | 复制 | 并行标记-整理 | 高吞吐,停顿长 |
| CMS | ParNew(复制) | 并发标记-清除 | 低延迟但有碎片 |
| G1(JDK 9+ 默认) | Region 复制 | Region 间标记-整理 | 可预测停顿 |
| ZGC / Shenandoah | 同堆 | 并发标记-整理 + 读屏障 | <10ms 停顿 |
- 核心权衡三角
- 吞吐 vs 停顿 vs 空间——三者不可兼得
- 新生代选复制(存活少)= 用空间换吞吐
- 老年代选标记-整理(无碎片)= 用停顿换空间
- 并发 GC 用写屏障/读屏障 = 用复杂度换停顿
第二部分:考察方向与延伸递进
考察方向:
- 深度:写屏障机制(Card Table、Remembered Set)、整理对象移动的成本
- 广度:各 GC 器的算法组合、ZGC/Shenandoah 的读屏障演进
- 应用:JDK 9+ 为什么 G1 成默认、什么时候换 ZGC、Eden 比例调优
延伸递进面试题:
- 为什么新生代用复制不用标记-清除?
- 解答要点:朝生夕死,存活少,复制快无碎片;标记-清除新生代虽快但碎片累积影响 Eden 分配
- Eden:Survivor = 8:1 是什么原理?
- 解答要点:JDK 8 默认 8:1:1,Survivor 只留约 10% 空间;存活多时(超 50%)直接晋升老年代;可通过
-XX:PretenureSizeThreshold 和 -XX:MaxTenuringThreshold 调优
- 写屏障是什么?为什么并发 GC 必需?
- 解答要点:写屏障是对象引用赋值时插入的额外代码(比如 CardTable 记录跨代指针),让 GC 能高效处理并发修改;ZGC 用读屏障(Load Barrier + 颜色指针)进一步减少 STW
- ZGC 如何实现 <10ms 停顿?
- 解答要点:并发标记+并发整理+读屏障+多映射(同一虚拟地址可映射多个物理地址,移动对象时改映射而非对象地址)
第三部分:面试者参考回答(300-600 字口语化)
面试官您好,JVM 的经典 GC 算法有三种:标记-清除、复制、标记-整理,然后现实 GC 器都是这三种算法在不同代上的组合。
标记-清除:从 GC Roots 遍历标记存活,再统一清理未标记。实现简单,但有内存碎片问题,长时间运行后大对象可能分配不到连续空间。
复制:堆分两半,GC 时把存活复制到另一半,清空旧半。优点是无碎片、存活少时极快,缺点是浪费一半空间,所以只适合新生代。HotSpot 优化成 Eden:From:To = 8:1:1——Eden 占 80%,两个 Survivor 各 10%,老年代吃满剩余空间;如果 Survivor 装不下就把对象提前晋升。
标记-整理:标记存活后,把存活对象压缩到一侧避免碎片。无碎片但要移动对象,移动时要处理所有引用(写屏障),STW 时间随老年代增长。所以适合老年代。
这三种算法怎么组合成了不同的 GC 器?
- ParallelGC(JDK 8 默认)= 新生代复制 + 老年代并行标记-整理,追求高吞吐
- CMS = ParNew 复制 + 并发标记-清除,追求低延迟但有碎片,JDK 14 弃用
- G1(JDK 9+ 默认)= Region 复制 + Region 间标记-整理,把整个堆切成等大的 Region,回收可以选最脏的 Region 优先,从而可预测停顿时间(
-XX:MaxGCPauseMillis)
- ZGC / Shenandoah = 并发标记-整理 + 读屏障 + 多映射,实现<10ms 停顿,堆可以到 TB 级
总结一句话:新生代选复制(存活少,无碎片),老年代选标记-整理(无碎片,用停顿换空间)。这个选择的基础就是弱分代假说——几乎所有对象朝生夕死。而并发 GC 想进一步缩短停顿,就必须引入写屏障、读屏障、颜色指针这些机制,本质还是这三种算法的演变。