在这里插入图片描述

面试题解答: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. 对象回收的两次标记过程
  1. 第一次标记:可达性分析发现不可达对象。
  2. 筛选阶段:检查对象是否覆盖 finalize() 方法:
    • ❌ 未覆盖:直接回收。
    • ✅ 已覆盖:将对象加入 F-Queue 队列,由低优先级线程执行 finalize()
  3. 第二次标记:若对象在 finalize() 中重新建立引用(如 this 赋值给全局变量),则移除回收列表;否则回收。

⚠️ 注意finalize() 不保证执行且影响性能,实践中应避免使用


5. 方法区回收

主要回收废弃常量无用的类,需同时满足:

  1. 该类的所有实例已被回收。
  2. 加载该类的 ClassLoader 已被回收。
  3. 该类对应的 java.lang.Class 对象未被引用(如反射调用)。

总结

  1. 核心机制:通过 GC Roots 遍历引用链,不可达对象即视为垃圾。
  2. 引用类型:强引用 > 软引用 > 弱引用 > 虚引用(回收优先级递增)。
  3. 两次标记:给覆盖 finalize() 的对象一次 “自救” 机会(但应避免依赖)。
  4. 方法区回收:条件苛刻,通常通过 Full GC 触发。

在这里插入图片描述

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐