新生代和老年代的 GC 算法有哪些?

#Java#面试八股

新生代和老年代的 GC 算法有哪些?

结论先行:新生代用复制算法(Eden + 两个 Survivor,默认 8:1:1),因为对象朝生夕死、存活率低,复制成本小且天然无碎片;老年代用标记-清除(CMS)或标记-整理(Serial Old、Parallel Old),因为存活率高,复制不划算且必须控制碎片。G1 之后不再严格分代,而是按 Region 动态选择「复制 + 整理」的混合回收。

一、两代算法对照

维度新生代老年代
算法复制(复制到 Survivor)标记-清除(CMS)/ 标记-整理(Serial Old、Parallel Old)
对象特征存活率低(< 10%)存活率高、对象大、寿命长
空间划分Eden、S0、S1一整块(或 G1 的 Region)
触发时机Eden 满老年代占用达阈值、晋升失败、空间分配担保失败
停顿短(几十毫秒以内)长(可达秒级)
关键参数-XX:SurvivorRatio、-XX:MaxTenuringThreshold-XX:CMSInitiatingOccupancyFraction、-XX:+UseCMSCompactAtFullCollection

二、新生代的对象流转

new 对象 → Eden(TLAB 优先)
   │ Eden 满
   ▼
Young GC:Eden + From Survivor 的存活对象 → 复制到 To Survivor
   │ 每次存活年龄 +1
   ├─ 年龄 ≥ MaxTenuringThreshold(默认 15)
   ├─ 同龄对象总和 > To Survivor 的一半(动态年龄判定)
   └─ 大对象直接进老年代(-XX:PretenureSizeThreshold)
   ▼
晋升老年代 → 老年代满触发 Full GC

三、复制算法为什么只浪费 10%

HotSpot 的默认比例是 Eden : S0 : S1 = 8 : 1 : 1,新生代总容量 10 份,每次只用一个 Survivor,另一份是空的「To 区」。

参数含义默认
-XX:SurvivorRatio=8Eden 与单个 Survivor 的比值8
-XX:MaxTenuringThreshold=15晋升年龄上限15
-XX:PretenureSizeThreshold超过该大小的对象直接进老年代0(不限制,仅 Serial/ParNew 有效)
-XX:TargetSurvivorRatio=90To 区期望使用率,影响动态年龄判定90

存活对象超过 To 区容量时走空间分配担保:先检查老年代最大连续可用空间是否足够,不够则提前触发 Full GC,这就是「担保失败」的由来。

四、用参数观察晋升行为

# 打印每次 Young GC 后各年龄段对象的分布,判断是否过早晋升
java -Xms1g -Xmx1g -Xmn400m \
     -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=10 \
     -Xlog:gc+age=trace -jar app.jar

# 关掉动态年龄判定,排除「同龄对象过半提前晋升」的干扰
java -XX:-UseAdaptiveSizePolicy -XX:MaxTenuringThreshold=15 -jar app.jar

gc+age=trace 输出形如 Desired survivor size 20971520 bytes, new threshold 10 (max 15) 加各年龄段的字节数,看到大量对象年龄很小就进入老年代,说明 Survivor 太小或对象存活时间超过预期。

五、常见追问

追问回答要点
为什么老年代不用复制算法存活率高时复制的对象量接近全量,还要额外一半空间,代价过高
为什么新生代要用两个 Survivor保证复制期间有一块完整空闲区,同时让对象在两次 GC 之间有机会「熬」到老年代
动态年龄判定的规则某年龄及更小年龄的对象总和超过 To 区一半,就把晋升阈值降到该年龄
大对象为什么直接进老年代避免在 Eden 与 Survivor 之间反复复制大块内存,-XX:PretenureSizeThreshold 可调
G1 还分新生代老年代吗逻辑上分(Young GC / Mixed GC),物理上是 Region,没有连续的两代空间

六、晋升失败与担保机制

场景触发条件结果
空间分配担保Young GC 前发现老年代连续可用空间不足先做一次 Full GC 腾空间
晋升失败(promotion failed)存活对象要进老年代,但没有连续空间触发 Full GC,通常伴随长停顿
大对象分配失败Humongous 对象找不到连续 RegionG1 退化为 Full GC

这就是「老年代碎片会让 Young GC 变成 Full GC」的机制来源:担保检查不通过时,一次本该 20ms 的 Young GC 可能直接变成 2s 的 Full GC。

小结

新生代复制、老年代清除或整理,是按存活率做出的经济选择;复制算法靠 8:1:1 把空间浪费控制在 10%,代价是必须处理晋升与担保。答题时把「存活率」作为解释主轴,逻辑就顺了。

笔记加载中…