精选·Java与并发

垃圾回收算法与 G1 收集器原理

91学AI·2026/7/13·9 阅读

考察点

GC 题分两层:基础层是三种算法和可达性分析,进阶层是具体收集器,G1 是现在面试的主流焦点(JDK 9 起是默认收集器)。面试官想确认你理解 G1 为什么能替代 CMS——Region 化、可预测停顿这两个关键词必须讲透。大数据岗位还会追问 Spark/Flink 作业的 GC 调优经验,以及什么时候该换 ZGC/Shenandoah。

参考答案

三种基础算法

标记-清除:标记出存活对象,回收剩下的。两个硬伤:效率随堆变大下降,清除后产生大量不连续碎片,大对象可能明明有内存却分配不出去。

复制算法:把内存分两半,用一半存一半,回收时把存活对象拷到另一半,整体清掉旧的一半。没有碎片,但浪费一半内存。新生代对象朝生夕死,所以实际做法是 Eden 加两个 Survivor(默认 8:1:1),每次只用 Eden 和一个 Survivor,存活对象复制到另一个 Survivor,浪费的只有 10%。

标记-整理:标记后让存活对象向一端移动,清掉边界外的内存。没有碎片也不浪费空间,但移动对象要更新所有引用,停顿长,适合老年代这种回收频率低、存活率高的区域。

对象生死判定

不用引用计数(解决不了循环引用),用可达性分析:从 GC Roots 出发,走不到的对象就是死的。GC Roots 包括栈帧里的局部变量、静态变量、JNI 引用、活动线程等。即使不可达也不是立刻死,finalize() 给了最后一次缓刑机会,但它不确定性太强,JDK 9 起已废弃,工程上没人依赖它。引用分强软弱虚四级,软引用适合内存敏感缓存(内存不够才回收),虚引用主要用来跟踪堆外内存回收。

G1 的设计核心

G1(Garbage First)把堆切成一组大小相等的 Region(每个 1MB 到 32MB,2 的幂,整个堆大约 2048 个),每个 Region 逻辑上扮演 Eden、Survivor、Old 或 Humongous(存超过半个 Region 的大对象)角色,不再物理连续分代。Region 之间用 Remembered Set 记录谁引用了我,避免全堆扫描。

回收流程四个阶段:

  1. 初始标记(STW,极短):只标记 GC Roots 直接关联的对象,借 Minor GC 顺带完成。
  2. 并发标记:和用户线程并行,遍历堆做可达性分析,用 SATB(原始快照)算法处理并发期间的引用变化。
  3. 最终标记(STW,短):处理 SATB 残留的少量变化。
  4. 筛选回收(STW):按每个 Region 的回收价值(回收空间/耗时)排序,挑收益最高的一批 Region,把存活对象复制到空 Region,整体释放旧 Region。这就是 Garbage First 名字的由来。

可预测停顿是卖点

G1 允许用 -XX:MaxGCPauseMillis(默认 200ms)设目标停顿时间,它会据此反推这次能回收多少个 Region——不是保证,是尽力逼近。对比 CMS:CMS 并发清除后产生碎片,碎片多到一定程度会触发 Full GC(Serial Old),一停就是几秒,这是它的致命伤;G1 的筛选回收本质是复制算法,边回收边整理,没有碎片问题。G1 的代价是内存开销大(RSet 和写屏障维护,可能占到堆的 10-20%),小堆(6-8GB 以下)用 G1 未必比 CMS/Parallel 划算。

工程上,大数据 executor 堆大(16GB 以上很常见),G1 基本是默认选择。调优套路:先看 GC 日志里 Full GC 频率和 Mixed GC 停顿,Humongous 分配频繁就调大 Region(-XX:G1HeapRegionSize);并发标记来不及就加 -XX:ConcGCThreads、提前 -XX:InitiatingHeapOccupancyPercent(默认 45%)。切忌只看平均值,P99 停顿才是用户体验。

可能的追问

  • Minor GC、Mixed GC、Full GC 的区别? Minor 只收新生代;Mixed 是 G1 特有,收所有新生代加一批高收益老年代 Region;Full GC 收整个堆,停顿最长,是要极力避免的。
  • 什么情况 G1 会退化 Full GC? 复制存活对象时找不到空 Region(Evacuation Failure),或并发标记没跑完老年代就满了(Concurrent Mark Failure),都会退化成单线程 Full GC。
  • ZGC 和 G1 怎么选? ZGC 停顿控制在 10ms 以内且与堆大小无关,适合超大堆(TB 级)低延迟场景;代价是吞吐略降、内存占用更高,且需要较新 JDK。一般大数据计算引擎 G1 够用。
  • 怎么判断是不是内存泄漏而不是 GC 问题? Full GC 后老年代占用持续走高不回落,基本可以确定泄漏;dump 堆后用 MAT 的 Dominator Tree 找占内存最大的引用链。

评论 (0)

暂无评论,快来抢沙发吧!

91学AI

© 2026 91学AI · 按岗位学 AI 与大数据. All rights reserved.