【Java-JVM】JVM 如何判断一个对象可被回收?
·

面试题解答:JVM如何判断一个对象可被回收?
1. 核心原理:可达性分析算法
JVM 通过可达性分析算法(Reachability Analysis) 判断对象是否存活。原理如下:
- GC Roots 作为起点:从一组称为 “GC Roots” 的特殊对象开始(如线程栈中的局部变量、静态变量、JNI 引用等),向下遍历引用链。
- 标记存活对象:所有能被 GC Roots 直接或间接引用的对象标记为存活。
- 回收不可达对象:未被标记的对象视为 “垃圾”,等待回收。
🔍 通俗比喻:
假设你参加一场派对(堆内存),主持人(GC Roots)拿着麦克风喊:“认识我的人举手!”。
- 直接举手的人(被 GC Roots 直接引用)是存活对象。
- 被举手者拉着的人(通过引用链间接引用)也是存活对象。
- 角落里无人理睬的人(不可达对象)会被保安(垃圾回收器)请出派对。
2. GC Roots 的具体类型
| GC Roots 类型 | 示例 |
|---|---|
| 虚拟机栈中的引用 | 局部变量:User user = new User() |
| 方法区静态属性引用 | public static User admin; |
| 方法区常量引用 | public static final String NAME = "JVM"; |
| 本地方法栈(JNI)引用 | Native 方法引用的对象 |
3. 引用类型与回收策略
Java 根据引用强度决定回收优先级:
| 引用类型 | 特点 | 回收时机 |
|---|---|---|
| 强引用 | 默认类型,如 Object obj = new Object() |
永不回收(除非显式置为 null) |
| 软引用 | SoftReference 包裹的对象(如缓存) |
内存不足时回收 |
| 弱引用 | WeakReference 包裹的对象(如 WeakHashMap 键) |
下次 GC 必回收 |
| 虚引用 | PhantomReference(仅用于跟踪对象被回收的通知) |
随时可能回收 |
📌 示例代码:
// 软引用示例(适合缓存)
SoftReference<byte[]> cache = new SoftReference<>(new byte[1024]);
if (cache.get() != null) {
// 内存充足时可用
} else {
// 内存不足时被回收
}
4. 对象回收的两次标记过程
- 第一次标记:可达性分析发现不可达对象。
- 筛选阶段:检查对象是否覆盖
finalize()方法:- ❌ 未覆盖:直接回收。
- ✅ 已覆盖:将对象加入
F-Queue队列,由低优先级线程执行finalize()。
- 第二次标记:若对象在
finalize()中重新建立引用(如this赋值给全局变量),则移除回收列表;否则回收。
⚠️ 注意:finalize() 不保证执行且影响性能,实践中应避免使用。
5. 方法区回收
主要回收废弃常量和无用的类,需同时满足:
- 该类的所有实例已被回收。
- 加载该类的
ClassLoader已被回收。 - 该类对应的
java.lang.Class对象未被引用(如反射调用)。
总结
- 核心机制:通过 GC Roots 遍历引用链,不可达对象即视为垃圾。
- 引用类型:强引用 > 软引用 > 弱引用 > 虚引用(回收优先级递增)。
- 两次标记:给覆盖
finalize()的对象一次 “自救” 机会(但应避免依赖)。 - 方法区回收:条件苛刻,通常通过 Full GC 触发。

更多推荐


所有评论(0)