垃圾回收中根据GCROOT判断对象是否可回收及Java中的引用类型
本文由 AI 生成,内容仅供参考,请仔细甄别。
一、如何判断一个对象是不是垃圾?
✅ Java 的判断标准:对象是否可达
可达性分析算法(Reachability Analysis):从一组被称为 “GC Roots” 的对象出发,沿着对象的引用链进行遍历,如果一个对象在遍历过程中无法被访问到,就被认为是“不可达”的,即为“垃圾”。
二、GC Roots 是哪些对象?
这些对象默认永远“活着”,作为可达性分析的起点:
- 虚拟机栈中的引用对象
虚拟机栈中的引用对象是指在方法调用的过程中,方法的局部变量引用的对象。每个线程在执行方法时,都会在虚拟机栈中创建一个栈帧,栈帧中的局部变量就是虚拟机栈中的引用对象。 - 方法区中的静态变量引用的对象
方法区中存放着类的相关信息,包括静态变量和常量池。当静态变量引用一个对象时,该对象就是一个 GC ROOT 对象。 - 方法区中的常量引用的对象
在常量池中的常量引用的对象也是 GC ROOT 对象,例如字符串常量池中的字符串对象。 - 本地方法栈中 JNI 引用的对象(如
native
方法使用的对象)
JNI 是 Java 调用本地方法的接口,本地方法栈中 JNI 引用的对象也是 GC ROOT 对象。 - 虚拟机内部的引用对象
虚拟机内部维护了一些对象,这些对象也是 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,配合队列使用 |