java引用类型WeakReference用法及原理详解
作者:看透也说透kevin
1. 核心概念:什么是 WeakReference?
WeakReference(弱引用)是 Java java.lang.ref 包下提供的一种引用类型。它的特点是:当垃圾回收器(GC)进行扫描时,无论当前内存是否充足,只要被弱引用关联的对象没有其他强引用**,那么这个对象就会被回收。
这与我们平时普通的对象引用(强引用,Strong Reference)形成鲜明对比:
- 强引用:
Object obj = new Object();。只要强引用存在,垃圾收集器就永远不会回收被引用的对象。 - 弱引用:
WeakReference<Object> weakRef = new WeakReference<>(obj);。即使弱引用存在,只要发生 GC 且对象没有强引用,它就会被回收。
2. 工作原理
WeakReference 的工作原理与 Java 的垃圾回收机制紧密相关。JVM 中的垃圾回收器使用可达性分析算法来判断对象是否存活。
可达性分析:
GC Roots 是所有引用链的起点(如虚拟机栈中的局部变量、静态变量等)。如果一个对象到 GC Roots 没有任何引用链相连,则证明此对象是不可用的。
引用级别(强度从高到低):
- 强引用 (Strong Reference):绝不回收。
- 软引用 (Soft Reference):内存不足时(OOM 前)才回收。
- 弱引用 (Weak Reference):发现即回收。只要 GC 发现一个对象只被弱引用指向,就会立刻将其标记为可回收对象,并在下一次 GC 时回收其内存。
- 虚引用 (Phantom Reference):对象回收跟踪。无法通过它获取对象实例,其唯一目的是在对象被回收时收到一个系统通知。
WeakReference 的工作流程可以简化为:
- 你创建一个对象,并用一个
WeakReference来包装它。 - 当你将对象的强引用置为
null(obj = null;)后,这个对象就只剩下WeakReference这一个“微弱”的引用了。 - 下一次垃圾回收发生时,GC 会发现这个对象是“弱可达”的。
- GC 会立即回收这个对象的内存。
- 回收之后,你的
WeakReference.get()方法将返回null。
3. 主要用法和示例
基本使用
import java.lang.ref.WeakReference;
public class WeakReferenceDemo {
public static void main(String[] args) {
// 1. 创建一个强引用对象
Object strongRef = new Object();
// 2. 用弱引用包装这个对象
WeakReference<Object> weakRef = new WeakReference<>(strongRef);
// 3. 此时既有强引用,也有弱引用。GC 不会回收它。
System.out.println("Before GC (Strong ref exists): " + weakRef.get()); // 有输出
// 4. 切断强引用!这是关键一步。
strongRef = null;
// 5. 建议系统进行垃圾回收(这只是建议,不保证立即执行)
System.gc();
// 6. 给 GC 一点时间执行
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 7. 检查弱引用指向的对象是否被回收
System.out.println("After GC (Only weak ref): " + weakRef.get()); // 输出 null
}
}
输出结果:
Before GC (Strong ref exists): java.lang.Object@1b6d3586
After GC (Only weak ref): null
典型应用场景:实现内存敏感的缓存
这是 WeakReference 最经典的使用场景。你可以用它来构建一个缓存,当内存不足时,缓存中的条目会被自动清除,从而避免内存泄漏(Memory Leak)。
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class SimpleCache<K, V> {
private final Map<K, WeakReference<V>> cache = new HashMap<>();
public void put(K key, V value) {
cache.put(key, new WeakReference<>(value));
}
public V get(K key) {
WeakReference<V> weakRef = cache.get(key);
if (weakRef != null) {
// 如果弱引用指向的对象还没被GC回收,就返回它
return weakRef.get();
}
// 如果被回收了或者不存在,就返回null
return null;
}
// 可以添加一个方法来清理Map中那些已经被GC回收的条目(key还在,value为null的Entry)
public void cleanUp() {
cache.entrySet().removeIf(entry -> entry.getValue().get() == null);
}
}
为什么这样用?
缓存的数据通常不是至关重要的。如果内存紧张,丢弃部分缓存数据是可以接受的,应用程序可以从原始数据源(如数据库)重新加载。使用 WeakReference 可以让 GC 充当你的“缓存清理工”,自动管理内存,你无需编写复杂的内存淘汰算法(如 LRU)。
注意: 上面的 SimpleCache 中的 Map 本身仍然持有 key 的强引用和 WeakReference 对象的强引用。如果 key 本身是一个大对象,你可能需要类似 WeakHashMap 的结构。
4. 注意事项
配合 ReferenceQueue 使用:你可以将一个
ReferenceQueue传递给WeakReference的构造函数。当弱引用指向的对象被垃圾回收后,这个WeakReference对象本身会被加入到这个队列中。这允许你执行一些后续的清理工作,例如从缓存Map中移除对应的键。ReferenceQueue queue = new ReferenceQueue<>();
WeakReference weakRef = new WeakReference<>(new Object(), queue);
// … 之后可以检查 queue.poll() 来获取被回收的引用不是银弹:
WeakReference主要用于缓解内存问题,而不是解决逻辑问题。你的代码必须能处理get()返回null的情况(即对象已被回收)。性能:频繁创建
WeakReference对象和进行 GC 本身也有开销,不要滥用。与 SoftReference 的区别:
WeakReference:GC 看到就回收,更激进。SoftReference:只有在内存不足(即将发生 OOM)时才会回收,更适合做缓存,因为能尽可能长时间地保留数据。而WeakReference的缓存被回收得更快。
总结
| 特性 | 描述 |
|---|---|
| 本质 | 一种不影响垃圾回收决策的引用类型。 |
| 核心原则 | 仅被弱引用关联的对象,在 GC 时必定被回收。 |
| 主要用途 | 实现规范化映射(如 WeakHashMap)、内存敏感的高速缓存、监听器列表等,防止因缓存等原因导致的内存泄漏。 |
| 如何使用 | 1. 创建 WeakReference 包装对象。2. 确保没有其他强引用指向该对象。 3. 在代码中总是检查 get() 是否返回 null。 |
到此这篇关于java引用类型WeakReference用法及原理的文章就介绍到这了,更多相关java WeakReference用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
