什么是强引用、软引用、弱引用和虚引用?
一则或许对你有用的小广告
欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书
《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于
Spring AI + Spring Boot 3.x + JDK 21...,查看介绍《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,查看介绍;演示链接:http://116.62.199.48:7070/《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/
新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍
截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观
面试考察点
-
基础掌握度:面试官不仅仅是想知道你能不能背出四种引用的名字,更是想看你能不能把每种引用的生命周期、回收时机、实际用途讲清楚。
-
GC 机制理解深度:四种引用和垃圾回收息息相关。如果你能说清楚 "SoftReference 在内存不足时才回收" 和 "WeakReference 下次 GC 就回收" 的区别,说明你真正理解了 GC 的工作机制,而不只是背了八股文。
-
实践应用意识:能不能举出实际使用场景,比如
WeakHashMap、ThreadLocal的弱引用设计、缓存方案中的SoftReference等,这是区分 "背过" 和 "用过" 的关键。
核心答案
Java 提供了 4 种引用级别,从强到弱依次是:
| 引用类型 | 回收时机 | 对应类 | 典型用途 |
|---|---|---|---|
| 强引用(Strong) | 永远不回收(只要引用还在) | 无(默认) | 日常编程中的普通引用 |
| 软引用(Soft) | 内存不足时回收 | SoftReference |
缓存 |
| 弱引用(Weak) | 下次 GC 时回收 | WeakReference |
WeakHashMap、ThreadLocal |
| 虚引用(Phantom) | 随时可能回收,无法获取对象 | PhantomReference |
跟踪 GC 回收、管理堆外内存 |
一句话:引用越 "弱",GC 对它越不留情面。
深度解析
一、强引用(Strong Reference)
强引用就是咱们日常写代码时最普通的引用:
Object obj = new Object(); // obj 就是一个强引用
只要强引用还存在,垃圾收集器就 绝对不会 回收被引用的对象,哪怕内存撑爆了抛出 OOM,也不会动它。这是最强硬的引用关系。
要想让对象能被回收,得手动断开引用:
obj = null; // 断开强引用,GC 才有可能回收该对象
二、软引用(Soft Reference)
软引用通过 SoftReference 类实现,用来描述一些 "还有用但非必需" 的对象:
SoftReference<byte[]> softRef = new SoftReference<>(new byte[1024 * 1024 * 10]);
// 获取对象
byte[] data = softRef.get();
// 如果内存不够了,GC 会自动回收,此时 get() 返回 null
回收规则:在内存充足时,软引用对象不会被回收;当内存不够用(即将 OOM)时,GC 才会把这些软引用指向的对象干掉。
这个特性让它非常适合做 内存敏感型缓存。比如图片缓存:内存够用的时候就留着,不够了自动清理,比 LRU 省心。不过说实话,现在生产环境用专门的缓存框架(Caffeine、Guava Cache)更靠谱,手写软引用缓存还是比较原始的方式。
三、弱引用(Weak Reference)
弱引用通过 WeakReference 类实现,比软引用更 "弱":
WeakReference<Object> weakRef = new WeakReference<>(new Object());
System.gc(); // 只要 GC 一跑
weakRef.get(); // 大概率返回 null 了
回收规则:无论内存是否充足,只要发生 GC,弱引用指向的对象就会被回收。
弱引用的经典应用场景有两个:
1. WeakHashMap
WeakHashMap<Key, Value> map = new WeakHashMap<>();
map.put(key, value);
// 当 key 没有强引用时,下次 GC 会自动清除该条目
// 这在实现 "临时映射" 或 "缓存映射" 时很有用
2. ThreadLocal 的实现
ThreadLocalMap 中的 Entry 的 key 是 WeakReference<ThreadLocal<?>>,这样当 ThreadLocal 对象没有强引用时,key 会被 GC 回收,避免内存泄漏。不过 value 还是强引用,仍然需要手动 remove(),这个坑很多人踩过。
四、虚引用(Phantom Reference)
虚引用是最弱的引用,通过 PhantomReference 类实现:
ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
phantomRef.get(); // 永远返回 null!
虚引用有几个特点:
- 无法通过
get()获取对象:永远返回null,这是它和软引用、弱引用最本质的区别 - 必须配合
ReferenceQueue使用:当对象被回收后,虚引用会被加入队列 - 唯一作用:在对象被回收时收到一个 "通知",用来做资源清理
虚引用最典型的应用是 跟踪堆外内存的释放。比如 DirectByteBuffer 分配了堆外内存,当对象被 GC 回收后,通过虚引用配合 ReferenceQueue 触发 Cleaner 机制来释放堆外内存。JDK 9+ 的 Cleaner 类就是基于虚引用实现的。
五、引用级别对比图
上图展示了四种引用从强到弱的关系。整体来看:
- 强引用 是最 "霸道" 的,只要引用还在,GC 宁可 OOM 也不回收它
- 软引用 比较温和,会根据内存状况做取舍,内存够就留着,不够就让步
- 弱引用 更加佛系,GC 一来就主动让位,不管内存够不够
- 虚引用 已经完全放弃了持有权,只关心 "你回收了没" 这个通知
关键点在于:引用越弱,GC 对该对象的回收越积极;而虚引用甚至不让你再访问对象,只提供一个回收通知机制。
六、ReferenceQueue 的作用
软引用、弱引用、虚引用都可以配合 ReferenceQueue 使用:
ReferenceQueue<Object> queue = new ReferenceQueue<>();
SoftReference<Object> softRef = new SoftReference<>(new Object(), queue);
// 当对象被 GC 回收后,softRef 会被自动加入 queue
// 通过监控 queue,可以知道哪些对象被回收了
Reference<?> ref = queue.poll();
if (ref != null) {
// 对象已被回收,执行清理逻辑
}
这在缓存场景中很实用——你不用遍历所有引用检查 get() 是否为 null,直接从队列里拿被清理的引用就行。
面试高频追问
-
ThreadLocal为什么用弱引用?还会内存泄漏吗?ThreadLocalMap的 Entry 的 key 是弱引用,当ThreadLocal对象没有外部强引用时,key 会被 GC 回收变为null。但 value 仍然是强引用,如果不手动调用remove(),value 就一直存在,造成内存泄漏。所以用完ThreadLocal一定要remove(),这条规则我强调多少次都不嫌多。 -
软引用和弱引用在实际项目中怎么用?
软引用常用于图片缓存、页面缓存等 "有了更好,没有也能工作" 的场景。弱引用常用于
WeakHashMap做 Canonicalizing Map、监听器列表等。不过实际项目中,更推荐用成熟的缓存框架(Caffeine 支持软/弱引用策略),手写的容易出 bug。 -
虚引用有什么实际用途?一般开发用得到吗?
日常业务开发基本用不到虚引用。它的主要使用者是 JDK 自身——比如
Cleaner机制用来释放堆外内存,或者实现比finalize()更安全的资源清理方案。面试官问这个,主要是看你有没有研究过 JDK 底层机制。
常见面试变体
- "为什么需要四种引用?只有强引用不行吗?"
- "
SoftReference和WeakReference的区别是什么?分别在什么场景下使用?" - "
ThreadLocal是如何防止内存泄漏的?" - "
ReferenceQueue有什么用?"
记忆口诀
强软弱虚,由强到弱:
- 强:宁死不屈,OOM 都不放手
- 软:见机行事,内存不够才放手
- 弱:随 GC 去,一回收就放手
- 虚:形同虚设,只管通知不持有
总结
四种引用本质上是给开发者提供了 和 GC 协商对象生命周期 的能力。强引用完全掌控、软引用商量着来、弱引用主动让步、虚引用只当旁观者。面试中把每种引用的回收时机和典型场景说清楚,再带上 ThreadLocal 和 Cleaner 的例子,基本就是满分回答了。
