Skip to content

05 - GC 机制

定位: JVM 的自动内存管理器——对象生死的判定者、堆空间的整理者 面试高频度: ⭐⭐⭐⭐⭐

一、GC 在 JVM 体系中的位置

GC (Garbage Collection) 是 Java 实现自动内存管理的核心设施。它与运行时数据区中的堆紧密耦合:

  • 上游: 接收 **对象创建过程** 的产出——堆上不断产生新对象
  • 核心: 判定哪些对象已经"死亡"(不再被任何引用指向),回收它们占用的内存
  • 下游: 影响 **OOM排查方法论** 和 **FullGC频繁排查方法论**——GC 是调优和排查的焦点

GC 解决了 C/C++ 中 malloc/free 手动管理内存的三大痛点:内存泄漏(忘记 free)、野指针(free 后继续使用)、双重释放(free 两次)。

┌─────────────────────────────────────────────────────────────────────┐
│                 GC 在 JVM 运行中的位置                                 │
│                                                                     │
│  类加载 → 对象创建 → 对象使用 → 对象不再被引用 → GC 回收 → 空间复用  │
│  (02)    (04)      (运行时)    (可达性分析)      (回收)    (堆)      │
│                              ↓                                       │
│                         GC 介入的时机:                               │
│                         1. 堆空间不足 (Allocation Failure)           │
│                         2. System.gc() (显式触发)                    │
│                         3. JVM 内部决策 (老年代满、Metaspace不够)     │
└─────────────────────────────────────────────────────────────────────┘

二、GC 演化时间线

每一代 GC 都是为了解决前一代在特定场景下的痛点:

JDK 1.3     Serial        ← 单线程。最简单,适合单核/小堆 (<100MB)
JDK 1.4.2   ParNew        ← Serial 的多线程版 Young GC,配合 CMS
JDK 1.4.2   Parallel      ← 多线程 Young+Old,追求吞吐量。JDK 8 默认
JDK 1.5     CMS           ← 并发老年代 GC。追求低延迟,但有"并发失败"硬伤
JDK 7u4     G1 (实验)     ← 分 Region,可预测停顿。JDK 9+ 默认
JDK 11      ZGC           ← 彩色指针 + 负载屏障,<10ms 停顿。JDK 21+ 支持分代
JDK 12      Shenandoah    ← Brooks 指针并发压缩,<10ms 停顿


        趋势: 单线程 → 多线程 → 并发 → 几乎全并发
              STW 停得越来越短,GC 对应用越来越透明

三、子专题导航

#主题面试频率核心内容
**对象存活判定算法**对象存活判定算法⭐⭐⭐⭐⭐引用计数 vs 可达性分析、GC Roots、四种引用类型、finalize()
**GC基本算法(标记清除与复制与标记整理)**GC 基本算法⭐⭐⭐⭐⭐标记-清除、标记-复制、标记-整理,三种算法的对比和选择逻辑
**垃圾收集器详解(Serial到ZGC)**垃圾收集器详解⭐⭐⭐⭐⭐Serial→ZGC 各收集器架构、CMS缺陷、G1 Region、ZGC 彩色指针
**GC日志分析**GC 日志分析⭐⭐⭐⭐日志格式解读、关键指标、常见模式排查、GCViewer/GCEasy
**GC调优参数速查**GC 调优参数速查⭐⭐⭐⭐堆/代/收集器参数、G1/ZGC 专属参数、生产配置模板、决策树

四、核心考点速记

1. 可达性分析与 GC Roots

当前 JVM 判定对象"活着"的标准:

GC Roots ──→ 直接引用对象 ──→ 间接引用对象 ──→ ……
   │                │                │
   ▼                ▼                ▼
  存活             存活             存活
  (递归遍历对象图。从 Roots 能到达的 → 活;不能到达的 → 死)

GC Roots 集合(面试高频——至少要说出前 4 类)

类型内容为什么是 Root
虚拟机栈引用局部变量表、参数中引用的对象当前正在执行的方法必须存活的上下文
静态属性引用Class 静态字段引用的对象类的全局状态,GC 不应回收
常量引用运行时常量池中的引用字符串字面量、final 常量值
JNI 引用native 方法栈中的 global/weak 引用Java 传入本地代码的对象
同步锁synchronized 持有的对象锁对象被回收 → 锁状态丢失
JVM 内部Class 对象、ClassLoader、异常对象JVM 正常运行的基础设施

2. 三色标记算法

并发标记的核心算法(G1/ZGC/CMS 都基于此):

白色 (White)     → 未被 GC 访问 → 可能死亡
灰色 (Gray)      → 已访问但未扫描完引用 → 中间态
黑色 (Black)     → 已访问且所有引用已扫描 → 确定存活

初始状态:                          标记完成:
┌─────┬─────┬─────┐               ┌─────┬─────┬─────┐
│ 白  │ 白  │ 白  │               │ 黑  │ 黑  │ 白  │
│     │     │     │   → 标记后 →   │     │     │(死亡)│
│ 白  │ 白  │ 白  │               │ 黑  │ 黑  │ 白  │
└─────┴─────┴─────┘               └─────┴─────┴─────┘

并发标记的 Two-Finger Problem 黑色对象新增指向白色对象的引用 → 白色被误标记为死亡 → 对象丢失。解决方案:增量更新(CMS)或 SATB(G1 的 Snapshot-At-The-Beginning)。

3. 各收集器一句话对比

收集器核心思想适用场景最大痛点
Serial单线程 STW客户端、小堆多核 CPU 浪费
Parallel多线程追求吞吐批处理、离线计算停顿时间长
CMS并发标记清除Web 服务器并发失败→串行 Full GC
G1分区+可预测停顿通用服务 (JDK 9+ 默认)大堆 (<64GB) 不如 ZGC
ZGC彩色指针+全并发超低延迟、超大堆内存开销稍高
ShenandoahBrooks 指针并发压缩超低延迟吞吐量略低于 ZGC

五、面试高频追问一览

追问关联笔记频次
JVM 怎么判断一个对象已死?**对象存活判定算法**极高
四种引用类型的区别和使用场景?**对象存活判定算法**极高
GC Roots 有哪些?**对象存活判定算法**极高
三种基本 GC 算法各自的优缺点?**GC基本算法(标记清除与复制与标记整理)**极高
CMS 的优缺点?为什么被 G1 替代?**垃圾收集器详解(Serial到ZGC)**极高
G1 相比 CMS 有哪些改进?**垃圾收集器详解(Serial到ZGC)**极高
ZGC 为什么这么快?**垃圾收集器详解(Serial到ZGC)**
怎么选择垃圾收集器?**垃圾收集器详解(Serial到ZGC)**
怎么看 GC 日志?Full GC 频繁怎么办?**GC日志分析**
你说一下常用的 GC 调优参数?**GC调优参数速查**

📚 相关链接

  • **Java堆** — GC 的操作对象,堆的分代结构是理解 GC 的基础
  • **对象内存布局(MarkWord与Klass与实例数据)** — MarkWord 中的 GC age 位
  • **分配策略(栈上分配与TLAB与大对象)** — GC 前的对象分配策略
  • **OOM排查方法论** — GC 失效导致的堆 OOM
  • **FullGC频繁排查方法论** — GC 异常导致的性能问题
  • ← 返回 **JVM文库设计文档**

Knowledge4J — Java 知识库