强引用、软引用、弱引用、虚引用有什么区别?各自用在哪里?

结论先行:四种引用按“被回收的难易程度”从难到易排列:强 > 软 > 弱 > 虚。强引用只要可达就不回收;软引用在内存不足、即将 OOM 前被回收,适合做缓存;弱引用在下一次 GC 就会被回收,适合“可有可无”的关联;虚引用拿不到对象本体,只用于对象被回收时收到通知,常配合 ReferenceQueue 做堆外资源清理。

四种引用对比表

引用类型回收时机能否取到对象典型用途
强引用可达即不回收new 出来的普通对象
软引用 SoftReference内存不足时(OOM 前)图片/大对象缓存
弱引用 WeakReference下一次 GCThreadLocal 的 key、WeakHashMap
虚引用 PhantomReference回收后通知(对象已不可见)不能堆外内存、文件句柄的回收钩子

示例代码

Object obj = new Object();
WeakReference<Object> wr = new WeakReference<>(obj);
System.gc();                       // 触发 GC 后,wr.get() 大多为 null

// 软引用:内存充足时仍能取到对象,作为缓存候选
SoftReference<byte[]> sr = new SoftReference<>(new byte[1024 * 1024]);

与 ThreadLocal / 缓存的关联

  • ThreadLocalMap 的 Entry key 是弱引用:key 没了,value 仍可能被 Thread 这条强引用链持有,所以用完必须 remove();
  • 做缓存时应选“软/弱引用 + 容量上限 + 过期时间”的组合,而不是用静态 Map 强引用所有对象。

选型小结

一句话:软引用给“可省则省的缓存”,弱引用给“可随时丢弃的关联”,虚引用给“回收通知”。

  • 图片/大对象缓存 → SoftReference,内存不足自动让路;
  • 缓存键与外部对象弱关联 → WeakHashMap、WeakReference;
  • 堆外内存与文件句柄回收 → PhantomReference + ReferenceQueue;
  • 普通业务对象保持强引用即可,不必刻意改用弱引用。

配合 ReferenceQueue 的写法

ReferenceQueue<Object> queue = new ReferenceQueue<>();
WeakReference<Object> wr = new WeakReference<>(obj, queue);
// 之后可轮询 queue.remove(),在对象被回收后做清理动作

常见追问 / 记忆点

  • 追问:WeakHashMap 的 key 为什么设计成弱引用?答:key 失去强引用后自动被清出,避免 Map 长期霸占 key 对象造成泄漏。
  • 追问:弱引用和虚引用都配合 ReferenceQueue,区别在哪?答:弱引用队列是“对象即将回收”的通知;虚引用是对象已回收后给清理钩子用的。
  • 记忆点:口诀“强软弱虚,回收由难到易”;软引用救缓存、弱引用防泄漏、虚引用管通知。
笔记加载中…