📌 PDF :大白话说Java面试题 --- 02-JVM篇
第24题:强引用、软引用、弱引用、虚引用分别是什么
📚 回答:
- 核心考点 :
四种引用的本质区别在于GC的可达性判定强度 ,直接影响对象的回收时机。大厂面试要求能结合实际场景(缓存、内存泄漏、堆外内存)说明。
1. 强引用(Strong Reference)
-
定义 :
Object obj = new Object()这类普通赋值就是强引用。只要存在强引用链,GC 永不回收该对象。 -
特点:
- 内存耗尽抛出
OutOfMemoryError也不会回收。 - 是99%代码中使用的引用类型。
- 内存耗尽抛出
-
风险:
- 集合(如
HashMap)中忘记remove导致内存泄漏 (典型场景:ThreadLocal的key是弱引用但value是强引用)。
- 集合(如
2. 软引用(Soft Reference)
-
定义 :
SoftReference<T>包装的对象,仅当JVM内存不足(即将OOM)时才被回收。 -
回收时机(大厂细节):
- 不是一内存紧张就收,而是在OOM之前 ,按 LRU(最近最少使用) 策略回收。
- JDK 的
-XX:SoftRefLRUPolicyMSPerMB参数控制:默认1000ms/MB,存活超过此时间的软引用优先回收。
-
典型应用:
- 内存敏感缓存:图片缓存、大对象缓存。
- 示例:
SoftReference<Bitmap> bitmapRef = new SoftReference<>(bitmap);
-
注意 :
软引用本身需要手动清理,否则可能伴随
ReferenceQueue内存泄漏。
3. 弱引用(Weak Reference)
-
定义 :
WeakReference<T>包装的对象,只要发生GC,就会被回收(无论内存是否充足)。 -
回收时机:
- 每次GC(Young GC 或 Full GC)后,弱引用对象都会被清除。
-
典型应用:
- 容器中的规范用法 :
WeakHashMap(key 是弱引用,自动删除过期的key)。 - ThreadLocal :
ThreadLocalMap的 key 是弱引用,防止线程长期存活导致ThreadLocal无法被回收。 - 短期监听器:避免注册后忘记解注册。
- 容器中的规范用法 :
-
对比软引用:
场景 软引用 弱引用 GC时是否回收 内存不足才收 每次GC都收 适合场景 缓存(内存允许就留着) 元数据/临时关联(生命周期更短)
4. 虚引用(Phantom Reference)
-
定义 :
PhantomReference<T>包装的对象,无法通过get()获取对象 (始终返回 null),仅用于感知对象即将被回收。 -
强制要求 :
必须配合
ReferenceQueue使用,对象被回收时虚引用被入队。 -
回收时机 :
对象被GC确定可回收后、内存回收前,虚引用入队。此时对象已无法复活。
-
典型应用:
- 堆外内存(DirectByteBuffer)清理 :
DirectByteBuffer分配堆外内存,通过PhantomReference+ReferenceQueue在对象GC时调用Cleaner回收堆外内存。 - 资源释放:文件句柄、网络连接等(虽然不建议,但可作为兜底)。
- 堆外内存(DirectByteBuffer)清理 :
-
关键区别(虚引用 vs 弱引用):
- 弱引用还能在GC前拿到对象(
get()不为 null); - 虚引用任何时候都拿不到对象,只有"即将回收"的信号。
- 弱引用还能在GC前拿到对象(
5. 汇总对比表
| 引用类型 | 回收时机 | 能否通过 get() 拿到对象 |
必须配合 ReferenceQueue |
典型场景 |
|---|---|---|---|---|
| 强引用 | 永不回收 | 是 | 否 | 常规对象 |
| 软引用 | OOM前(内存不足) | GC前可以 | 可选 | 缓存(图片、大对象) |
| 弱引用 | 下次GC | GC前可以 | 可选 | WeakHashMap、ThreadLocal |
| 虚引用 | 对象GC时 | 否(永远null) | 是 | 堆外内存清理、资源释放 |
6. 大厂面试追问
Q1:软引用什么时候被回收?能够保证一定在OOM前被回收吗?
A:不能100%保证。如果对象非常大,且分配速度极快,可能还没来得及回收软引用就已经OOM。因此软引用缓存需配合硬限制(如最大缓存条目数)。
Q2:WeakHashMap 能完全避免内存泄漏吗?
A:不能。WeakHashMap 只对 key 弱引用,如果 key 对象被回收,value 仍然存在且无法访问(除非手动清理)。真正的内存泄漏常见于 value 强引用链条未断。
Q3:虚引用为什么无法复活对象?
A:虚引用对象入队时,对象的 finalize() 已执行过(若有),且不可能再被任何强引用关联。JVM 故意设计为无法复活,保证清理动作安全。
Q4:如何手动触发软/弱引用清理?
A:通过 ReferenceQueue 轮询,调用 queue.remove() 或 queue.poll() 取出引用,然后主动 clear()。
Q5:四种引用的底层实现原理?
A:JVM 通过 oop(对象指针) 中 引用类型标记位 区分强度。GC 时 ReferenceProcessor 根据标记和 ReferenceQueue 分阶段处理:强 > 软 > 弱 > 虚。
7. 实战代码示例(面试可手写)
java
// 软引用示例
SoftReference<byte[]> cache = new SoftReference<>(new byte[10 * 1024 * 1024]);
System.out.println(cache.get()); // 在内存充足时打印对象
// 弱引用 + ReferenceQueue 示例
ReferenceQueue<Object> queue = new ReferenceQueue<>();
WeakReference<Object> weakRef = new WeakReference<>(new Object(), queue);
System.gc();
System.out.println(weakRef.get()); // 大概率 null
System.out.println(queue.poll()); // 等于 weakRef(入队了)
// 虚引用示例
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
System.out.println(phantomRef.get()); // 永远 null
💡 面试官想要的满分总结:
"四种引用的强度递减:强 → 软 → 弱 → 虚。
强引用 永不清除;软引用 内存不足才清,适合缓存;弱引用 每次GC必清,适合WeakHashMap/ThreadLocal;虚引用 永远拿不到对象,专用于感知GC时机,典型场景是 NIO 的DirectByteBuffer清理堆外内存。生产需注意:
SoftReference缓存的 LRU 策略由 JVM 控制,不可依赖精确清除时机;WeakHashMap也需主动管理value的生命周期。"
觉得对您有帮助,麻烦 点点关注啦 ,您的关注是我创作的最大动力~ 🎯