在Java中,强引用、软引用、弱引用和虚引用是用于管理对象生命周期的不同类型的引用。它们的主要作用是帮助垃圾回收器(GC)决定何时回收对象,从而更高效地管理内存。
强软弱虚,按照这个顺序,对象的引用的强度越来越弱。
强引用是Java的默认引用形式,使用时不需要显示定义。如果一个对象具有强引用,那垃圾回收器绝不会回收它(可达时)。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
String[] arr = new String[]{"a", "b", "c"};
强引用是默认的引用方式,我们创建的new 一个对象,就是用的强引用,即如果引用一直在,就不会被 GC 回收掉。
在集合中, 如我们常见的HashMap
使用的引用就是强引用,也就是说垃圾收集的时候,Map中引用的对象不会被GC掉。
软引用是一种相对较弱的引用类型,用于表示对对象的“非强”引用。
软引用是使用SoftReference来创建对象的方式,如SoftReference<Object> softRef = new SoftReference<>(new Object());
就是软引用。
当内存不足时,垃圾回收器会优先回收软引用指向的对象,以释放内存。软引用可以在系统内存充足时保留对象,以提高性能(缓存机制)。
SoftReference<String[]> softBean = new SoftReference<String[]>(new String[]{"a", "b", "c"});
在 Guava Cache 中,有关于软引用的使用:
class LocalCache<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V> {
static class SoftValueReference<K, V> extends SoftReference<V> implements ValueReference<K, V> {
final ReferenceEntry<K, V> entry;
SoftValueReference(ReferenceQueue<V> queue, V referent, ReferenceEntry<K, V> entry) {
super(referent, queue);
this.entry = entry;
}
}
}
LocalCache
类用到了 SoftReference
来实现软引用缓存。通过软引用,来方便在内存不足时能自动回收缓存对象,来避免 OOM 的发生。
弱引用比软引用更弱。被弱引用指向的对象只能存活到下一次垃圾回收之前。弱引用是使用WeakReference来创建对象的方式,WeakReference<Object> weakRef = new WeakReference<>(new Object());
WeakReference<Object> weakBean = new WeakReference<Object>(new Object());
如果一个对象只有弱引用指向,即使系统内存充足,垃圾回收器也会立即回收它。
JDK中有一种基于弱引用类型实现的HashMap——WeakHashMap。
public class WeakHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
//省略部分方法和属性
private static class Entry<K, V> extends WeakReference<Object> implements Map.Entry<K, V> {
V value;
final int hash;
Entry<K, V> next;
Entry(Object var1, V var2, ReferenceQueue<Object> var3, int var4, Entry<K, V> var5) {
super(var1, var3);
this.value = var2;
this.hash = var4;
this.next = var5;
}
}
}
这里面的Entry 就是一种WeakReference,即弱引用,也就是说,当WeakHashMap中的 key已经不再被使用的时候,Entry 因为是弱引用,那么就会被自动回收掉。
另外,为了避免内存泄漏,在 ThreadLocal 中也用到了弱引用:
虚引用是最弱的一种引用。虚引用的存在不会影响对象的生命周期,垃圾回收器在回收对象时不会考虑虚引用。虚引用是使用PhantomReference来创建对象的方式,
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), referenceQueue);
虚引用主要用于跟踪对象被回收的状态,并在对象被回收后进行一些后续处理(如清理本地资源等)。
虚引用通常与 ReferenceQueue
结合使用,管理清理逻辑。它在很多需要资源清理的场景中使用,比如文件句柄、数据库连接等。
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.nio.ByteBuffer;
public class PhantomReferenceExample {
// 定义一个简单的资源管理类
static class ResourceCleaner {
private final ReferenceQueue<ByteBuffer> queue;
private final PhantomReference<ByteBuffer> ref;
public ResourceCleaner(ByteBuffer buffer) {
// 创建一个 ReferenceQueue
this.queue = new ReferenceQueue<>();
// 创建一个虚引用并与 ReferenceQueue 关联
this.ref = new PhantomReference<>(buffer, queue);
}
public void cleanUp() {
// 在某个时间点调用清理方法
PhantomReference<ByteBuffer> polledRef = (PhantomReference<ByteBuffer>) queue.poll();
if (polledRef != null) {
// 进行资源释放操作
System.out.println("Cleaning up resources...");
// 释放与 ByteBuffer 相关的资源
// 在这里可以进行额外的清理工作,如关闭文件句柄等
}
}
}
public static void main(String[] args) {
// 创建一个直接内存缓冲区
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
// 创建 ResourceCleaner 并传入缓冲区
ResourceCleaner cleaner = new ResourceCleaner(buffer);
// 断开对缓冲区的强引用
buffer = null;
// 手动触发垃圾回收(为了示例,实际使用中不建议如此操作)
System.gc();
// 调用清理方法,检查是否有虚引用已被回收并进行清理
cleaner.cleanUp();
}
}
特性 | 强引用 | 软引用 | 弱引用 | 虚引用 |
---|---|---|---|---|
生命周期 | 最长 | 次于强引用 | 次于软引用 | 次于弱引用 |
OOM前被清理 | 否 | 是 | 是 | 是 |
GC前被清理 | 否 | 否 | 是 | 是 |