JAVA八股文第三章(JVM 中如何判断对象可以被回收)
JVM 中如何判断对象可以被回收?(可达性分析彻底讲清)
在 Java 中,我们不需要手动释放内存,因为 JVM 会通过GC(垃圾回收)机制自动回收对象。
但一个核心问题是:
JVM 是如何判断一个对象“可以被回收”的?
这篇文章将从原理到实现,彻底讲清这个问题。
一、核心结论(一句话版)
一个对象如果“无法从 GC Roots 访问到”,就可以被回收二、两种判断方式(历史 & 现状)
在理论上,有两种判断对象是否存活的方法:
1️⃣ 引用计数法(Reference Counting)❌
原理:
每个对象有一个引用计数器 被引用 → +1 引用失效 → -1 计数为 0 → 可回收❗ 问题:
无法解决“循环引用”示例:
Aa=newA();Bb=newB();a.b=b;b.a=a;👉 结果:
- a 和 b 互相引用
- 计数都不为 0
- 但实际上已经“没用了”
👉结论:Java 不使用引用计数法
2️⃣ 可达性分析(Reachability Analysis)✅
👉 JVM 实际使用的方法
三、什么是可达性分析?
👉 核心思想:
从一组“根对象(GC Roots)”出发, 看对象是否能被访问到✔ 判断规则:
- 能访问到 → 存活
- 访问不到 → 可回收
四、什么是 GC Roots?
👉 GC Roots 是一组“永远不会被回收的起点对象”
常见 GC Roots:
1. 虚拟机栈中的局部变量(引用) 2. 方法区中的静态变量 3. 方法区中的常量 4. 本地方法栈中的引用👉 简单理解:
GC Roots = 所有“正在使用的入口”五、判断过程(图解思维)
假设内存中有这样一个结构:
GC Roots → A → B → C D → E(没有任何引用指向)分析:
- A、B、C:可以从 GC Roots 访问 → 存活
- D、E:不可达 → 可回收
👉 本质:
只要断开与 GC Roots 的连接,就会被回收六、再深入:对象一定会被立即回收吗?
👉 不一定!
对象回收需要两次判断:
✔ 第一次:
是否可达(是否有引用)✔ 第二次:
是否需要执行 finalize()finalize 机制:
@Overrideprotectedvoidfinalize()throwsThrowable{// 对象“自救”}👉 特点:
- 对象在 finalize 中可以“复活”
- 只会执行一次
- 不推荐使用(已逐步废弃)
👉 结论:
即使不可达,也不一定立即回收七、引用类型(高级面试点)
Java 中不仅有“强引用”,还有 4 种引用类型:
1️⃣ 强引用(Strong Reference)
Objectobj=newObject();👉 特点:
只要有强引用,就不会被回收2️⃣ 软引用(Soft Reference)
👉 内存不足时才回收
3️⃣ 弱引用(Weak Reference)
👉 只要发生 GC 就会被回收
4️⃣ 虚引用(Phantom Reference)
👉 用于跟踪对象回收(几乎不用)
八、完整判断流程(总结版)
1. 从 GC Roots 出发进行可达性分析 2. 判断对象是否可达 3. 若不可达 → 标记为“可回收” 4. 判断是否需要执行 finalize 5. 若未“复活” → 最终回收九、面试高频问题总结
✔ JVM 如何判断对象是否可以被回收?
👉 可达性分析算法
✔ 为什么不用引用计数法?
👉 无法解决循环引用问题
✔ 什么是 GC Roots?
👉 一组作为起点的对象(栈、静态变量等)
✔ 对象一定会被立即回收吗?
👉 不一定(finalize 机制)
✔ 强引用会被回收吗?
👉 不会(只要存在)
十、终极总结
判断对象是否回收的本质: 看它是否“还能被用到”更技术一点:
是否能从 GC Roots 可达十一、结语
理解“对象如何被回收”,是掌握 JVM 的关键一步:
- 想学 GC → 必须懂可达性分析
- 想优化内存 → 必须懂引用类型
- 想应对面试 → 这是必考题
如果你能把本文吃透,JVM 内存机制已经迈入进阶阶段。