在 Java 中,JVM 使用 垃圾回收器 (GC) 来自动管理内存。JVM 判断一个对象是否可以被回收的主要依据是 对象是否可达。具体来说,如果某个对象不再被任何可达的引用所引用,那么这个对象就可以被认为是 垃圾,可以被回收。
判断一个对象是否可以被回收的标准:
- 可达性分析 (Reachability Analysis):
- JVM 使用 可达性分析 算法来判断一个对象是否可以被回收。如果从 GC Root(垃圾回收根对象)开始,无法通过任何引用链访问到该对象,则该对象是不可达的,可以被回收。
- GC Root通常包括以下几种对象:
- 当前线程的栈帧(局部变量)
- 方法区中的类和常量池
- 活跃的线程等
- 引用类型:
- 强引用:最常见的引用类型。只要对象有强引用,它就不会被回收。
- 软引用:对象在内存不足时可以被回收。
- 弱引用:当垃圾回收器进行回收时,弱引用指向的对象会被回收。
- 虚引用:无法通过虚引用访问到对象,只能用来在对象被回收时收到通知。
举例代码
1. 强引用
public class StrongReferenceExample {
public static void main(String[] args) {
Object obj = new Object(); // obj 是对 Object 的强引用
obj = null; // 强引用解除,obj 变为 null,但对象还未被回收
}
}
在这个例子中,obj
最初指向一个 Object
实例。如果没有其他引用指向这个对象,那么在 obj = null
后,这个对象成为不可达对象,最终会被垃圾回收器回收。
2. 软引用
import java.lang.ref.SoftReference;
public class SoftReferenceExample {
public static void main(String[] args) {
Object obj = new Object();
SoftReference<Object> softRef = new SoftReference<>(obj);
obj = null; // 解除强引用
// 在内存不足时,软引用指向的对象会被回收
System.gc(); // 请求进行垃圾回收
Object recoveredObj = softRef.get(); // 获取软引用的对象
if (recoveredObj != null) {
System.out.println("软引用指向的对象仍然存在");
} else {
System.out.println("软引用指向的对象已经被回收");
}
}
}
在这个例子中,SoftReference
提供了一种引用类型,当内存紧张时,JVM 会回收软引用指向的对象。
3. 弱引用
import java.lang.ref.WeakReference;
public class WeakReferenceExample {
public static void main(String[] args) {
Object obj = new Object();
WeakReference<Object> weakRef = new WeakReference<>(obj);
obj = null; // 解除强引用
// 弱引用对象在下次垃圾回收时会被回收
System.gc(); // 请求进行垃圾回收
Object recoveredObj = weakRef.get(); // 获取弱引用的对象
if (recoveredObj != null) {
System.out.println("弱引用指向的对象仍然存在");
} else {
System.out.println("弱引用指向的对象已经被回收");
}
}
}
在这个例子中,WeakReference
指向的对象会在下一次垃圾回收时被回收,即使它还有弱引用指向它。
4. 虚引用
虚引用不用于访问对象,它只能和 ReferenceQueue
一起使用,通常用于在对象被回收时执行一些清理工作。
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class PhantomReferenceExample {
public static void main(String[] args) {
Object obj = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(obj, referenceQueue);
obj = null; // 解除强引用
System.gc(); // 请求进行垃圾回收
// 检查虚引用是否已经入队
if (phantomRef.isEnqueued()) {
System.out.println("虚引用指向的对象已经被回收");
} else {
System.out.println("虚引用指向的对象没有被回收");
}
}
}
在这个例子中,PhantomReference
是一种特殊的引用类型,允许在对象被回收后通知应用程序执行一些清理工作。
引用计数法和可达性分析法
JVM 常用的垃圾回收算法是 可达性分析法,它检查对象的引用链。另一种经典的算法是 引用计数法,但这种方法容易出现 循环引用 的问题。
可达性分析法:这是当前大部分 JVM 的实现方式。它从 GC Root 出发,追踪对象图,通过图中的引用关系判断对象是否可达。
- 如果对象 A 是 GC Root 可达的,且对象 A 引用了对象 B,B 也可达。
- 如果对象 C 不可达(没有被 GC Root 引用),则对象 C 被认为是垃圾。
引用计数法:这种方法给每个对象添加一个引用计数器,当计数器为 0 时,对象可被回收。但这种方法无法处理循环引用的问题,因此在实际中较少使用。
总结
- JVM 判断对象是否可以回收,主要基于 可达性分析法,如果一个对象不可达(即没有任何引用链可以访问到该对象),它就可以被回收。
- 引用类型(强引用、软引用、弱引用、虚引用)会影响对象的回收行为。