在 Java 开发中,对象的引用类型直接影响内存管理和垃圾回收(GC)的行为。除了常见的强引用,Java 还提供了软引用(SoftReference
)、弱引用(WeakReference
)和虚引用(PhantomReference
),用于实现更灵活的内存管理策略。本文将深入解析这四种引用类型,并通过多个代码示例展示其实际应用场景。
一、强引用(Strong Reference)
1. 基本概念
强引用是默认的引用类型,只要对象存在强引用,垃圾回收器就不会回收该对象。只有当强引用被显式置为 null
或超出作用域时,对象才会被标记为可回收。
Object obj = new Object(); // 强引用
obj = null; // 取消强引用,对象可被回收
2. 应用场景
-
常规对象创建,如业务逻辑中的核心对象。
-
需要长期驻留内存的数据(如配置信息)。
二、软引用(SoftReference)
1. 基本概念
软引用描述的对象在内存不足时会被回收。当 JVM 发现内存吃紧时,会清理软引用指向的对象。这种特性使得软引用非常适合实现内存敏感的缓存。
2. 代码示例:图片缓存
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;
public class ImageCache {
private Map<String, SoftReference<Bitmap>> cache = new HashMap<>();
public Bitmap getImage(String key) {
SoftReference<Bitmap> ref = cache.get(key);
if (ref != null) {
Bitmap bitmap = ref.get();
if (bitmap != null) {
return bitmap;
}
}
// 重新加载图片
Bitmap bitmap = loadImageFromDisk(key);
cache.put(key, new SoftReference<>(bitmap));
return bitmap;
}
private Bitmap loadImageFromDisk(String key) {
// 模拟从磁盘加载图片
return new Bitmap();
}
static class Bitmap {
// 图片数据
}
}
应用场景:
-
缓存图片、临时数据等,避免 OOM。
-
需要内存不足时自动释放的非关键数据。
三、弱引用(WeakReference)
1. 基本概念
弱引用比软引用更弱,被弱引用关联的对象只能存活到下一次 GC。无论内存是否充足,弱引用对象都会被回收。
2. 代码示例:监听器列表
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public class EventManager {
private List<WeakReference<EventListener>> listeners = new ArrayList<>();
public void addListener(EventListener listener) {
listeners.add(new WeakReference<>(listener));
}
public void fireEvent() {
for (WeakReference<EventListener> ref : listeners) {
EventListener listener = ref.get();
if (listener != null) {
listener.onEvent();
}
}
}
public interface EventListener {
void onEvent();
}
}
应用场景:
-
避免监听器或回调导致的内存泄漏(如 Android 中的
Handler
)。 -
WeakHashMap
实现临时键值存储(键被回收时条目自动删除)。
四、虚引用(PhantomReference)
1. 基本概念
虚引用是最弱的引用类型,无法通过虚引用访问对象。其唯一目的是在对象被回收时收到系统通知(通过 ReferenceQueue
)。虚引用必须与引用队列(ReferenceQueue
)配合使用。
2. 代码示例:资源清理
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class ResourceCleanupExample {
private static class Resource {
@Override
protected void finalize() throws Throwable {
System.out.println("Resource finalized");
}
}
public static void main(String[] args) {
ReferenceQueue<Resource> queue = new ReferenceQueue<>();
PhantomReference<Resource> phantomRef =
new PhantomReference<>(new Resource(), queue);
// 触发 GC
System.gc();
try {
// 等待虚引用被加入队列(最多等待500ms)
java.lang.ref.Reference<? extends Resource> ref = queue.remove(500);
if (ref != null) {
System.out.println("Resource被回收,执行清理操作");
// 清理资源(如关闭文件句柄、释放Native内存)
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Resource finalized
Resource被回收,执行清理操作
应用场景:
-
追踪对象被回收的时机,执行最终的资源清理(如释放堆外内存)。
-
结合
ReferenceQueue
实现精细化的对象生命周期管理。
五、四种引用对比
引用类型 | 回收时机 | 用途 | 访问对象 |
---|---|---|---|
强引用 | 永不回收(除非置为null) | 常规对象 | 直接访问 |
软引用 | 内存不足时回收 | 缓存 | 通过get()访问 |
弱引用 | 下次GC时回收 | 防止内存泄漏(如监听器) | 通过get()访问 |
虚引用 | 对象被回收后收到通知 | 资源清理 | 无法访问 |
六、总结
-
强引用:默认选择,用于长期存活的对象。
-
软引用:适合内存敏感的缓存,避免OOM。
-
弱引用:防止内存泄漏,如监听器、
WeakHashMap
。 -
虚引用:资源清理和对象回收跟踪。
合理利用不同引用类型,可以在内存管理和性能优化之间找到平衡。实际开发中,应根据业务场景选择最合适的引用类型,避免滥用导致不可预见的性能问题。