如何判断对象可以被回收
JVM 判断对象是否可回收的核心是 “对象是否再被引用”,主要通过 “可达性分析算法” 实现,同时结合 “引用类型” 和 “finalize () 方法” 辅助判断,具体流程如下:
# 1. 核心算法:可达性分析(Reachability Analysis)
- 原理:从 “GC Roots”(一组活跃的引用)出发,遍历对象的引用链;若一个对象无法通过任何 GC Roots 到达(即引用链断裂),则该对象被标记为 “不可达”,视为可回收对象。
- GC Roots 的类型:
- 线程栈中正在执行的方法的局部变量表引用的对象(如方法参数、局部变量);
- 方法区中静态变量引用的对象(如
static Object obj = new Object()); - 方法区中常量引用的对象(如
final Object obj = new Object()); - 本地方法栈中 JNI(Java Native Interface)引用的对象(如调用 C++ 方法时的引用);
- 活跃线程对象(如正在运行的 Thread 对象);
- 被同步锁(synchronized)持有的对象。
# 2. 引用类型:影响对象回收时机
Java 中的引用分为 4 种类型(从强到弱),不同引用类型的对象,回收时机不同:
- 1. 强引用(Strong Reference):
- 最常见的引用(如
Object obj = new Object()),只要强引用存在,对象就不会被回收; - 即使内存不足,JVM 也会抛出
OutOfMemoryError,不会回收强引用对象。
- 最常见的引用(如
- 2. 软引用(Soft Reference):
- 通过
SoftReference类实现(如SoftReference<Object> softRef = new SoftReference<>(new Object())); - 当内存充足时,对象不被回收;当内存不足(即将发生 OOM)时,JVM 会回收软引用对象;
- 应用场景:内存敏感的缓存(如图片缓存,内存不足时自动清理)。
- 通过
- 3. 弱引用(Weak Reference):
- 通过
WeakReference类实现(如WeakReference<Object> weakRef = new WeakReference<>(new Object())); - 无论内存是否充足,只要发生 GC(YGC 或 FGC),弱引用对象都会被回收;
- 应用场景:临时缓存(如 ThreadLocal 中的 value,避免内存泄漏)。
- 通过
- 4. 虚引用(Phantom Reference):
- 通过
PhantomReference类实现,必须与ReferenceQueue配合使用; - 虚引用不影响对象的回收,仅在对象被回收时,将虚引用加入
ReferenceQueue,用于跟踪对象的回收状态; - 应用场景:管理直接内存(如 NIO 的 DirectBuffer,回收时释放直接内存)。
- 通过
# 3. 辅助判断:finalize () 方法的 “救赎”
- 原理:每个对象都有
finalize()方法(定义在Object类中),当对象被可达性分析标记为不可达后,JVM 会先将对象放入 “F-Queue” 队列,由低优先级的 “Finalizer 线程” 执行对象的finalize()方法; - “救赎” 机会:在
finalize()方法中,若对象重新建立与 GC Roots 的引用(如obj = this),则对象会被移出 “待回收” 状态,避免被回收; - 注意事项:
finalize()方法仅执行一次,若对象再次被标记为不可达,不会再执行finalize();finalize()方法执行时间不确定(可能被阻塞),且效率低,不推荐使用,推荐用try-finally或AutoCloseable管理资源。
# 4. 特殊情况:对象的 “自我救赎” 失效
- 若对象的
finalize()方法未重写,或已执行过一次,即使在finalize()中重新建立引用,也无法避免被回收; - 若 “Finalizer 线程” 执行
finalize()方法时抛出异常,异常会被忽略,对象仍会被回收。
# 总结
JVM 判断对象可回收的流程:
- 通过可达性分析,标记不可达对象;
- 根据引用类型,确定对象的回收优先级(强引用 > 软引用 > 弱引用 > 虚引用);
- 若对象是不可达且无强引用,执行
finalize()方法,给予 “救赎” 机会; - 若 “救赎” 失败,对象被确定为 “可回收”,等待 GC 时被清理。
上次更新: 12/30/2025