垃圾回收中根据GCROOT判断对象是否可回收及Java中的引用类型

本文由 AI 生成,内容仅供参考,请仔细甄别。


一、如何判断一个对象是不是垃圾?

✅ Java 的判断标准:对象是否可达

可达性分析算法(Reachability Analysis):从一组被称为 “GC Roots” 的对象出发,沿着对象的引用链进行遍历,如果一个对象在遍历过程中无法被访问到,就被认为是“不可达”的,即为“垃圾”。


二、GC Roots 是哪些对象?

这些对象默认永远“活着”,作为可达性分析的起点:

  1. 虚拟机栈中的引用对象
    虚拟机栈中的引用对象是指在方法调用的过程中,方法的局部变量引用的对象。每个线程在执行方法时,都会在虚拟机栈中创建一个栈帧,栈帧中的局部变量就是虚拟机栈中的引用对象。
  2. 方法区中的静态变量引用的对象
    方法区中存放着类的相关信息,包括静态变量和常量池。当静态变量引用一个对象时,该对象就是一个 GC ROOT 对象。
  3. 方法区中的常量引用的对象
    在常量池中的常量引用的对象也是 GC ROOT 对象,例如字符串常量池中的字符串对象。
  4. 本地方法栈中 JNI 引用的对象(如 native 方法使用的对象)
    JNI 是 Java 调用本地方法的接口,本地方法栈中 JNI 引用的对象也是 GC ROOT 对象。
  5. 虚拟机内部的引用对象
    虚拟机内部维护了一些对象,这些对象也是 GC ROOT 对象,例如系统类加载器、线程、JNI 引用等。

三、示意图理解可达性分析

GC Roots
   |
   v
obj1 → obj2 → obj3
          ↑
         obj4

obj5 (没有任何对象引用它,也无法被 GC Roots 访问)→ 垃圾

在上图中:

  • obj1 ~ obj4 是可达的,因此不是垃圾
  • obj5 无法通过 GC Roots 找到引用链 → 被判定为垃圾

四、特殊情况:对象的“自救”机制(finalize)

在一次 GC 判断中,被判定为垃圾的对象并不一定马上被回收。

  • 如果对象重写了 finalize() 方法,并且该方法中让这个对象重新与 GC Roots 建立了关联,它可以“逃脱死亡”一次。
  • finalize() 只会被调用 一次

示例代码:

public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        SAVE_HOOK = this;  // 关键:重新建立引用
        System.out.println("finalize 被调用!");
    }

    public static void main(String[] args) throws Throwable {
        SAVE_HOOK = new FinalizeEscapeGC();
        SAVE_HOOK = null;
        System.gc();  // 第一次 GC
        Thread.sleep(500);
        System.out.println(SAVE_HOOK != null);  // true

        SAVE_HOOK = null;
        System.gc();  // 第二次 GC
        Thread.sleep(500);
        System.out.println(SAVE_HOOK != null);  // false(因为 finalize 只被调用一次)
    }
}

五、总结:Java 如何判断对象是不是垃圾?

判断方式 说明
可达性分析(主流) 沿 GC Roots 向下遍历,无法访问到的对象即为垃圾
引用计数(旧方案) 不再使用,容易造成循环引用无法回收问题
finalize 自救机制 第一次回收前可能通过 finalize 复活,但只限一次

好的,下面我们来详细讲解 Java 中四种引用类型(强、软、弱、虚)垃圾回收判定 之间的关系,这对理解对象是否会被 GC 回收非常关键。


六、Java 四种引用类型与 GC 的关系

引用类型 是否影响 GC 判定 回收时机 常见用途
强引用(StrongReference) ❌ 不会被回收 永不回收(除非显式设为 null) 普通对象引用
软引用(SoftReference) ✅ 可能被回收 内存不足时回收 缓存,例如图片缓存
弱引用(WeakReference) ✅ 很容易被回收 下一次 GC 时就回收 ThreadLocal、元数据缓存等
虚引用(PhantomReference) ✅ 必定会被回收 被 GC 标记为垃圾时就被回收(不会自动清理) 对象被回收前收到通知(用于资源释放)

1、强引用(StrongReference)

最常用的引用类型,例如:

Object obj = new Object(); // obj 是对 Object 实例的强引用

只要有一个强引用存在,对象就不会被 GC 回收。


2、软引用(SoftReference)

SoftReference<Object> softRef = new SoftReference<>(new Object());

GC 时,如果内存充足,该对象不会被回收;如果内存不足,才会被回收。

应用场景:

  • 常用于实现 内存敏感缓存,如图片、页面数据等。

3、弱引用(WeakReference)

WeakReference<Object> weakRef = new WeakReference<>(new Object());

一旦发生 GC,不管内存是否足够,只要没有强引用关联,它就会被回收。

应用场景:

  • Java 的 ThreadLocal 的内部结构使用了弱引用。
  • 一些元数据缓存,例如 ClassLoader 缓存。

4、虚引用(PhantomReference)

PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), referenceQueue);

虚引用本身不会阻止对象被回收。它 唯一的用途是:对象被标记为可回收时,将虚引用放入 ReferenceQueue,用于追踪对象生命周期。

应用场景:

  • 管理堆外内存
  • 在对象被回收前进行一些 资源清理操作
  • 实现比 finalize() 更可靠的清理方式(避免 finalize 的副作用)

六、GC 判定中四种引用的作用总结

引用类型 会被 GC 判断为垃圾吗? 是否影响对象可达性分析 特点
强引用 是(参与可达性分析) 永不被回收(除非断开引用)
软引用 是(视内存而定) 内存不足时会被回收
弱引用 是(随 GC) GC 一来就回收
虚引用 不会阻止 GC,配合队列使用

posted @ 2025-06-05 21:30  MuXinu  阅读(67)  评论(0)    收藏  举报