强引用、软引用、弱引用、虚引用有什么区别?各自用在哪里?
结论先行:四种引用按“被回收的难易程度”从难到易排列:强 > 软 > 弱 > 虚。强引用只要可达就不回收;软引用在内存不足、即将 OOM 前被回收,适合做缓存;弱引用在下一次 GC 就会被回收,适合“可有可无”的关联;虚引用拿不到对象本体,只用于对象被回收时收到通知,常配合 ReferenceQueue 做堆外资源清理。
四种引用对比表
| 引用类型 | 回收时机 | 能否取到对象 | 典型用途 |
|---|---|---|---|
| 强引用 | 可达即不回收 | 能 | new 出来的普通对象 |
| 软引用 SoftReference | 内存不足时(OOM 前) | 能 | 图片/大对象缓存 |
| 弱引用 WeakReference | 下一次 GC | 能 | ThreadLocal 的 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,区别在哪?答:弱引用队列是“对象即将回收”的通知;虚引用是对象已回收后给清理钩子用的。
- 记忆点:口诀“强软弱虚,回收由难到易”;软引用救缓存、弱引用防泄漏、虚引用管通知。