JVM 中一次完整的 GC 流程是怎样的?

#Java#面试八股

JVM 中一次完整的 GC 流程是怎样的?

结论先行:以 G1 为例,一次完整的 GC 周期不是「一次停顿」,而是Young GC → 并发标记周期 → 混合回收(Mixed GC)→ 兜底 Full GC 的组合。对象先在 Eden 分配,Eden 满触发 Young GC;当老年代占用超过阈值(默认 45%)启动并发标记,标记完成后进入若干轮混合回收,逐步清掉老年代中垃圾占比高的 Region;如果回收速度追不上分配速度,才会退化成一次 Full GC。

一、完整周期

对象分配(Eden / TLAB)
   │ Eden 满
   ▼
① Young GC(STW):Eden + Survivor 存活对象 → 新 Survivor 或晋升
   │ 老年代占用 ≥ InitiatingHeapOccupancyPercent(默认 45%)
   ▼
② 并发标记周期
   ├─ 初始标记(STW,借一次 Young GC 完成)
   ├─ 根区域扫描(并发)
   ├─ 并发标记(并发,SATB 写前屏障)
   ├─ 重新标记(STW,处理 SATB 队列)
   └─ 清理(并发,统计各 Region 存活率)
   ▼
③ 混合回收(多轮 Mixed GC):Young Region + 垃圾占比高的 Old Region
   │ 若晋升速率 > 回收速率,或 Humongous 分配失败
   ▼
④ Full GC(STW,单线程标记-整理,兜底)

二、各阶段在做什么

阶段是否 STW主要动作日志关键字
Young GC是复制 Eden/Survivor 存活对象,处理晋升Pause Young (Normal)
初始标记是标记 GC Roots 直接可达对象Pause Young (Concurrent Start)
并发标记否遍历对象图,SATB 记录引用删除Concurrent Mark Cycle
重新标记是处理 SATB 缓冲,完成最终标记Pause Remark
清理否统计 Region 存活率,回收全空 RegionConcurrent Cleanup
混合回收是回收 Young + 部分 Old RegionPause Young (Mixed)
Full GC是整堆标记-整理,压缩Pause Full

三、日志里的一个真实周期

[2.315s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 512M->48M(1024M) 12.431ms
[5.902s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 560M->96M(1024M) 15.207ms
[8.441s][info][gc] GC(2) Pause Young (Concurrent Start) (G1 Evacuation Pause) 604M->148M(1024M) 18.002ms
[8.441s][info][gc] GC(2) Concurrent Mark Cycle
[9.120s][info][gc] GC(2) Pause Remark 172M->172M(1024M) 2.114ms
[9.402s][info][gc] GC(2) Pause Cleanup 176M->176M(1024M) 0.089ms
[9.883s][info][gc] GC(3) Pause Young (Mixed) (G1 Evacuation Pause) 420M->132M(1024M) 21.553ms

三段信息要会读:512M->48M(1024M) 是「回收前 → 回收后(堆总容量)」,Pause Young (Concurrent Start) 表示这次 Young GC 顺带启动了并发标记,Mixed 说明这一轮带上了老年代 Region。

四、让一次完整周期跑出来

# 小堆 + 低阈值,几秒内就能看到完整的 Young / Mixed / Full 链路
java -Xms256m -Xmx256m \
     -XX:+UseG1GC \
     -XX:InitiatingHeapOccupancyPercent=20 \
     -XX:G1HeapRegionSize=4m \
     -Xlog:gc*:stdout:time,uptime \
     -jar gc-demo.jar

# 想观察兜底 Full GC:让晋升速率超过回收速率即可
java -Xms128m -Xmx128m -XX:+UseG1GC -XX:G1ReservePercent=5 -jar gc-demo.jar

五、常见追问

追问回答要点
为什么初始标记要借一次 Young GCG1 利用 Young GC 的 STW 窗口顺带完成,避免额外停顿,所以日志里显示 (Concurrent Start)
SATB 解决什么问题并发标记期间引用被删除时,按快照把旧引用记下来,保证不漏标
混合回收会回收所有 Old Region 吗不会,只回收「垃圾占比高于阈值」的 Region,分多轮完成,这就是 MaxMixedGcCount 的意义
Full GC 一定是灾难吗是信号不是原因:说明收集器已经失控,要查 Humongous 分配、晋升速率、-XX:G1ReservePercent
CMS 的完整流程有何不同CMS 没有 Mixed 概念,直接「初始标记 → 并发标记 → 重新标记 → 并发清除」,兜底是带压缩的 Full GC

小结

一次完整 GC 周期是「复制回收新生代 → 并发标记老年代 → 分批混合回收 → 失败则整堆兜底」。能把日志行对应到阶段,就已经超过大多数只会背名词的答案了。

笔记加载中…