JVM 垃圾回收(GC)

一、第一性原理层(Why)

1. 为什么需要垃圾回收

根本问题

程序如何判断一块内存是否还“有价值”?

在现代高级语言中:

如果完全依赖人工释放:

结论

垃圾回收的本质,是把“内存生命周期管理”从业务逻辑中剥离,交由运行时系统统一治理。


2. GC 的本质定义

无论实现如何变化,所有 GC 都在解决同一个问题:

在不破坏程序语义的前提下,找出仍然可能被使用的对象,其余内存可以被安全重用。

抽象为两个不可分割的子问题:

  1. **存活对象发现(Live Object Discovery)**
  2. **内存空间重用(Space Reclamation & Reuse)**

二、理论模型层(What)

3. 对象“是否存活”的理论判定

3.1 引用计数模型(被否定的方案)

思想

被否定的根本原因

JVM 放弃该方案,并非性能问题,而是正确性难以保证


3.2 可达性分析模型(GC 的理论基石)

核心思想

对象是否存活,不取决于“被引用次数”,而取决于是否仍可被程序访问

模型定义:

这是 JVM GC 的第一性理论基础。


4. GC Roots 的哲学含义

GC Roots 不是随意选择,而是满足一个原则:

只要程序还能“直接使用”的对象,就必须作为根。

因此包括:


5. 引用强度模型(对象生存策略)

引用不是二元的(有 / 无),而是一个策略梯度

引用类型 本质定位 设计目的
强引用 必须存活 程序正确性
软引用 可牺牲 内存弹性(缓存)
弱引用 不阻止回收 生命周期绑定
虚引用 仅做通知 资源回收协调

引用模型的本质,是让程序参与“内存价值排序”


三、分代与对象行为假说(Why + What)

6. 分代收集的理论前提

JVM 并非随意分代,而是基于三条经验假说:

  1. **弱分代假说**:绝大多数对象朝生夕灭
  2. **强分代假说**:活得越久的对象越难死亡
  3. **跨代引用假说**:跨代引用远少于同代引用

GC 分代不是优化技巧,而是对象行为统计学


7. 分代的抽象角色划分

本质角色 回收目标
新生代 高死亡率区 快速回收
老年代 高稳定区 减少频率

分代的真正目的:

用不同策略处理不同“生存概率”的对象。


四、核心回收算法(How)

8. 三大基础算法的哲学权衡

8.1 标记-清除(Mark-Sweep)

适用场景:


8.2 标记-复制(Mark-Copy)

本质适配:

高死亡率区域(新生代)


8.3 标记-整理(Mark-Compact)

哲学权衡:

停顿时间 vs 吞吐量


五、并发 GC 的不变量与工程约束

9. 为什么并发 GC 如此困难

根本冲突:

GC 在“看对象图”,而程序在“改对象图”。


10. 三色标记不变量

颜色 含义
未访问(可能垃圾)
已访问,未扫描完
已完全扫描

并发正确性的核心不变量

黑对象不能直接引用白对象


11. 两大并发修正思想

思想 代表 本质
增量更新 CMS 关注“新增引用”
原始快照(SATB) G1 / ZGC 固定起始视图

六、HotSpot 架构支撑机制(How)

12. Safepoint / Safe Region

GC 不是随时可做的,而必须在引用关系稳定点执行。

Safepoint 的本质:


13. OopMap

OopMap 解决的问题不是“快”,而是:

如何在暂停时准确知道哪里是引用。


14. 记忆集与卡表

设计目标:

避免全堆扫描老年代。

卡表是:


七、垃圾收集器 = 策略组合体

15. 衡量 GC 的三大指标

指标 含义
吞吐量 程序执行效率
延迟 停顿时间
内存占用 运行成本

GC 设计是典型的不可能三角问题


16. 典型收集器的设计定位

收集器 核心追求 代价
Serial 极简 长停顿
Parallel 吞吐 延迟不可控
CMS 低延迟 碎片
G1 可预测 实现复杂
ZGC 极低延迟 架构复杂

八、现代 GC 的演进趋势

17. 演进主线

对象规模 ↑ → 堆容量 ↑ → 人类容忍停顿 ↓

因此:

关联内容(自动生成)