YoungGC 和 FullGC 的触发条件是什么?
结论先行:Young GC 只有一个主条件——新生代 Eden 区放不下新对象;Full GC 的触发条件则有一串:老年代空间不足、晋升/担保失败、元空间扩容、显式 System.gc()、CMS 并发模式失败、G1 转移失败、堆转储请求。面试时要能按收集器分别说清,而不是笼统地讲「内存不够」。
一、Young GC 的触发
| 触发点 | 说明 |
|---|---|
| Eden 区满 | 最常见;新对象分配时 Eden 无足够连续空间 |
| TLAB 耗尽且 Eden 无法再切分 | 线程本地分配缓冲用尽,需要新的分配区域 |
| 大对象直接在 Eden 分配失败 | 超过 PretenureSizeThreshold 尝试直入老年代,失败则先做 Young GC |
Young GC 一定会 STW,但只扫描新生代与卡表(记忆集)里的跨代引用,所以停顿短。
二、Full GC 的触发
| 触发点 | 所属收集器 | 说明 |
|---|---|---|
| 老年代空间不足 | 全部 | 晋升或大对象分配时无连续空间 |
| 空间分配担保失败 | 全部 | Young GC 前检查老年代余量不够 |
| 晋升失败(promotion failed) | Parallel、CMS | 存活对象要晋升但没有空间 |
| 元空间达到阈值 | JDK 8+ | MetaspaceSize 触发的首次扩容前会先做一次 Full GC |
| 显式调用 | 全部 | System.gc()、Runtime.gc()、JConsole 的「执行 GC」 |
| CMS 并发模式失败 | CMS | 并发清理未完成老年代已满 |
| 转移失败 / Humongous 分配失败 | G1 | 没有空闲 Region 可搬 |
jmap -dump 或 HeapDumpBeforeFullGC | 全部 | 工具主动触发 |
分配新对象
│ Eden 有空间?
├─ 是 → 分配,结束
└─ 否 → Young GC(STW)
│ 存活对象晋升
├─ 老年代有余量 → 复制到老年代,结束
└─ 无余量 → 担保检查 → 失败则 Full GC(STW 更长)
三、按收集器区分
| 收集器 | Young GC 触发 | Full GC 触发 |
|---|---|---|
| Serial / Parallel | Eden 满 | 老年代不足、担保失败、System.gc() |
| CMS | ParNew 的 Eden 满 | 老年代占用 ≥ CMSInitiatingOccupancyFraction(默认 92%)时启动并发收集;并发模式失败则 Full GC |
| G1 | Eden Region 用尽 | 并发标记后进入 Mixed GC;转移失败或 Humongous 分配失败才 Full GC |
注意 CMS 的「老年代占用达阈值」启动的是并发收集而非 Full GC,只有并发收集追不上才算 Full GC——这是 CMS 与 Parallel 最容易被混淆的点。
四、把两种 GC 都逼出来
public class TriggerDemo {
public static void main(String[] args) throws Exception {
// 1. Young GC:快速产生大量短命对象,Eden 很快填满
for (int i = 0; i < 200_000; i++) {
byte[] tmp = new byte[1024]; // 立即失效,只占 Eden
}
// 2. Full GC:让对象晋升并在老年代堆积
java.util.List<byte[]> hold = new java.util.ArrayList<>();
for (int i = 0; i < 64; i++) {
hold.add(new byte[1024 * 1024]); // 累计超过 -Xmx 的一半,触发老年代回收
}
// 3. 显式 Full GC
System.gc();
System.out.println("held=" + hold.size());
}
}
# 小堆跑,几秒内即可看到 Pause Young 与 Pause Full
java -Xms64m -Xmx64m -Xmn16m -Xlog:gc* TriggerDemo
# 观察触发原因(JDK 11+)
jcmd <pid> GC.cause
五、常见追问
| 追问 | 回答要点 |
|---|---|
| Young GC 会清理老年代吗 | 不会,但会把存活对象晋升进去;老年代增长过快会加速后续 Full GC |
| 为什么元空间会触发 Full GC | 扩容前先尝试回收,回收不到才申请本地内存,避免无脑增长 |
System.gc() 到底做不做 | 默认做;-XX:+DisableExplicitGC 后变成空操作;-XX:+ExplicitGCInvokesConcurrent 改为并发 |
| G1 的 Mixed GC 算 Full GC 吗 | 不算,Mixed 只回收部分老年代 Region,是 G1 的正常回收;Full 才是兜底 |
| 怎么减少 Full GC | 降分配速率、调大 Survivor、避免大对象、修泄漏、别让元空间无限增长 |
小结
Young GC 只有「Eden 满」一个主因,Full GC 是一组失败路径的集合。答题时按「收集器 + 触发条件 + 日志关键字」三层展开,比背名词扎实得多。