JAVA八股文 第四章(JVM 垃圾回收算法)

JVM 垃圾回收算法详解(从基础到面试,一篇搞懂)

在 Java 中,我们不需要手动释放内存,但这并不意味着内存管理是"自动就简单"的。

JVM 通过一套复杂而高效的 垃圾回收(GC, Garbage Collection)算法 来管理内存。

本文将系统讲清:

  • 常见 GC 算法
  • 每种算法的原理与优缺点
  • JVM 为什么采用"分代回收"

一、什么是垃圾回收?

👉 简单来说:

text 复制代码
垃圾回收 = 找出"没用的对象"并释放内存

核心问题有两个:

text 复制代码
1. 如何判断对象是否死亡?
2. 如何回收这些对象?

👉 第一问已经解决(可达性分析)

👉 本文重点:第二问(回收算法)


二、三大基础垃圾回收算法


1️⃣ 标记-清除算法(Mark-Sweep)


👉 原理:

text 复制代码
第一步:标记所有"存活对象"
第二步:清除未标记对象(垃圾)

👉 图示理解:

text 复制代码
[对象1][对象2][垃圾][对象3][垃圾]
   ↓ 标记
[✔][✔][ ][✔][ ]
   ↓ 清除
[对象1][对象2][    ][对象3][    ]

✅ 优点:

  • 实现简单
  • 不需要移动对象

❌ 缺点:

text 复制代码
1. 内存碎片严重
2. 分配大对象困难

三、标记-复制算法(Copying)


👉 原理:

text 复制代码
把内存分成两块(A / B)

使用 A:
复制存活对象到 B
清空 A
交换 A / B

👉 图示:

text 复制代码
A区:[对象][垃圾][对象][垃圾]
↓ 复制
B区:[对象][对象]
↓ 清空
A区:空

✅ 优点:

text 复制代码
无内存碎片
分配速度快

❌ 缺点:

text 复制代码
内存利用率低(只能用一半)

四、标记-整理算法(Mark-Compact)


👉 原理:

text 复制代码
标记存活对象
把所有存活对象"往一边挪"
清理边界外空间

👉 图示:

text 复制代码
原始:[对象][垃圾][对象][垃圾][对象]
↓ 整理
[对象][对象][对象][      ][      ]

✅ 优点:

text 复制代码
无内存碎片
空间利用率高

❌ 缺点:

text 复制代码
移动对象成本高

五、为什么需要分代回收?

👉 这是 JVM 的核心优化思想!


📌 经验规律:

text 复制代码
大部分对象"朝生夕死"
少数对象长期存活

👉 所以 JVM 把堆分为:

text 复制代码
新生代(Young)
老年代(Old)

六、分代回收策略


1️⃣ 新生代(Young)

👉 特点:

text 复制代码
对象多,但存活率低

👉 使用算法:

text 复制代码
标记-复制算法

👉 原因:

  • 垃圾多 → 复制成本低
  • 追求速度

2️⃣ 老年代(Old)

👉 特点:

text 复制代码
对象少,但存活率高

👉 使用算法:

text 复制代码
标记-清除 或 标记-整理

👉 原因:

  • 不适合复制(太多对象要复制)
  • 更关注空间利用率

七、经典垃圾回收器(了解)

虽然算法是基础,但 JVM 实际用的是"回收器组合":


常见回收器:

  • Serial(单线程)
  • ParNew(多线程)
  • CMS(并发标记清除)
  • G1(分区回收)

👉 举例:

  • CMS:基于标记-清除(低停顿)
  • G1:结合复制 + 整理(现代主流)

八、面试高频总结


✔ 常见 GC 算法有哪些?

text 复制代码
标记-清除
标记-复制
标记-整理

✔ 各自优缺点?

👉 一句话总结:

text 复制代码
清除:简单但有碎片
复制:快但浪费空间
整理:无碎片但慢

✔ 为什么要分代?

text 复制代码
不同对象生命周期不同 → 用不同算法更高效

✔ 新生代为什么用复制算法?

text 复制代码
对象存活少 → 复制成本低

✔ 老年代为什么不用复制?

text 复制代码
存活对象多 → 复制成本高

九、终极总结

text 复制代码
GC 本质:
找到垃圾 → 用合适算法回收

再补一句更核心的:

text 复制代码
分代 + 不同算法组合 = JVM 高性能的关键

十、结语

垃圾回收算法是 JVM 的核心:

  • 理解它 → 才能理解 GC
  • 理解 GC → 才能优化性能
  • 优化性能 → 才能写高质量 Java 程序