WeakReference与SoftReference以及结合ReferenceQueue实践整理
文章目录
- 四种引用类型概述
- WeakReference与SoftReference区别
- ReferenceQueue详解
- 问题
- 生活场景类比
- 代码实例
- 使用场景
四种引用类型概述
强引用 (Strong Reference):是最常见的引用形式,通常我们通过 new 关键字创建的对象引用都是强引用。只要一个对象有强引用指向它,垃圾回收器就不会回收这个对象
Object obj = new Object(); // obj 是一个强引用
软引用 (Soft Reference):软引用是比强引用弱一些的引用类型,适合用来实现缓存。只有在内存不足时,垃圾回收器才会回收软引用指向的对象。使用 SoftReference 类来实现。例如:
SoftReference<Object> softRef = new SoftReference<>(new Object());
弱引用 (Weak Reference):比软引用更弱的引用类型。当垃圾回收器运行时,不论内存是否充足,只要发现某个对象只有弱引用指向它,就会回收该对象。可以使用 WeakReference 类来创建弱引用。例如:
WeakReference<Object> weakRef = new WeakReference<>(new Object());
虚引用 (Phantom Reference):最弱的引用类型,虚引用(PhantomReference):如果一个对象只能被 GC Root虚引用到,则和无法被GC Root引用到时一样。因此,就垃圾回收过程而言,虚引用就像不存在一样,并不会决定对象的生命周期。虚引用主要用来跟踪对象被垃圾回收器回收的活动。
WeakReference与SoftReference区别
WeakReference用于引用那些只有在没有强引用时才能被回收的对象。当一个对象只有WeakReference指向它而没有其他强引用时,垃圾收集器会在下一次进行垃圾回收时将其回收。这意味着,如果只有弱引用指向对象,那么该对象会被认为是不可达的,并在垃圾收集时被回收。
WeakReference通常用于构建高效的缓存、观察者模式等场景,其中需要及时回收对象而不会造成内存泄漏。当没有其他强引用指向对象时,这些弱引用指向的对象会被自动清理。
String myObject = new String("Hello");
WeakReference<String> weakReference = new WeakReference<>(myObject);
// 切断强引用
myObject = null;
// 在适当的时机,垃圾收集器会回收对象
// 以下代码中,weakReference.get() 有可能返回 null
SoftReference用于引用可能还有用但并非必需的对象。与弱引用不同的是,当垃圾收集器执行垃圾回收时,只有在内存不足的情况下,才会回收被软引用指向的对象。这使得软引用非常适合实现缓存。
在内存充足的情况下,即使只有软引用指向对象,对象仍然保持在内存中。但当内存不足时,垃圾收集器会尝试回收这些被软引用指向的对象,以便释放更多内存。
String myObject = new String("Hello");
SoftReference<String> softReference = new SoftReference<>(myObject);
// 切断强引用
myObject = null;
// 在内存充足的情况下,对象可能仍然存在于软引用中
// 但当内存不足时,垃圾收集器会回收对象
// 以下代码中,softReference.get() 在内存不足时可能返回 null
WeakReference适合用于只有在没有强引用时才能被回收的对象。
SoftReference适合用于在内存不足时才被回收的对象,通常用于实现缓存等功能。
请注意,在使用WeakReference和SoftReference时,需要根据具体情况小心地处理引用对象为空的情况,因为它们在垃圾回收时有可能返回null。
ReferenceQueue详解
问题
如果一个对象只有软引用或者弱引用,则它随时可能会被 JVM垃圾回收掉。这会给我们带来的问题是我们在使用时根本无法知道它是否还存在。
可是,有时我们需要知道被软引用或者弱引用的对象在何时被回收,以便进行一些后续的处理工作。ReferenceQueue类便提供了这样的功能。ReferenceQueue本身是一个列表,我们可以在创建软引用或者弱引用的包装对象时传入该列表。这样,当 JVM 回收被包装的对象时,会将其包装类加入 ReferenceQueue类中。
生活场景类比
想象你是一家大型图书馆的管理员,图书馆里有许多书籍供读者借阅和使用。你知道这些书籍并不永远留在书架上,有时候读者借阅了书籍后就会离开,把书带走。为了保持图书馆的秩序,你需要知道哪些书已经离开图书馆,并安排人手把书的位置腾空,以供新的书籍存放。
图书馆的书籍:代表内存中的对象。它们可以被“读者”(即程序中的引用)持有或者放回。
读者离开图书馆时归还书籍:就像对象被垃圾回收一样,书籍不再被使用或占有。
ReferenceQueue(通知队列):你设立了一个专门的通知队列,当一本书离开图书馆时,图书馆的系统会通知这个队列,告诉管理员这本书的位置已经空出来了,需要清理或重新分配空间。
• 每当有读者借走书籍(对象被引用),这本书就暂时不属于书架(内存中)。
• 当这本书被归还(对象变得不可达,被垃圾回收)时,系统会通知 ReferenceQueue,让管理员知道某个位置已经空出来了。
• 管理员检查 ReferenceQueue,发现有书的位置空出来了,就会把这个位置清理,腾出空间供其他书籍使用。
ReferenceQueue 的用途和作用:在垃圾回收发生时用来通知程序哪些对象已经被回收,并在需要时执行后续操作,比如清理或释放资源。
代码实例
public static void main(String[] args) {
simpleRef();
refWithReferenceQueue();
}
private static void simpleRef() {
// 通过等号直接建立的引用都是强引用
User user = new User();
// 通过SoftReference建立的引用是软引用
SoftReference<User> softRefUser =new SoftReference<>(new User());
// 通过WeakReference建立的引用是弱引用
WeakReference<User> weakRefUser = new WeakReference<>(new User());
}
private static void refWithReferenceQueue() {
// 创建ReferenceQueue
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 用来存储弱引用的目标对象
List<WeakReference> weakRefUserList = new ArrayList<>();
// 创建大量的弱引用对象,交给weakRefUserList引用
for (int i =0 ; i< 1000000; i++) { // 模拟内存不足
// 创建弱引用对象,并在此过程中传入ReferenceQueue
WeakReference<User> weakReference = new WeakReference(new User(Math.round(Math.random() * 1000)),referenceQueue);
// 引用弱引用对象
weakRefUserList.add(weakReference);
}
WeakReference weakReference;
Integer count = 0;
// 处理被回收的弱引用
while ((weakReference = (WeakReference) referenceQueue.poll()) != null) {
// 虽然弱引用存在,但是引用的目标对象已经为空
System.out.println("JVM 清理了:" + weakReference + ", 从WeakReference中取出对象值为:" + weakReference.get());
count ++;
}
// 被回收的弱引用总数
System.out.println("weakReference中的元素数目为:" + count);
// 在弱引用的目标对象不被清理时,可以引用到目标对象
System.out.println("在不被清理的情况下,可以从WeakReference中取出对象值为:" +
new WeakReference(new User(Math.round(Math.random() * 1000)),referenceQueue).get());
}
static class User {
private long id;
public User() {
}
public User(long id) {
this.id = id;
}
@Override
public String toString() {
return "User:" + id;
}
}
使用场景
【1】内存敏感的缓存:使用 SoftReference 来实现缓存机制,可以让应用在内存不足时自动回收缓存对象。例如,图像缓存、数据缓存等,当内存紧张时,JVM 会自动回收不再被强引用的缓存对象,从而减少内存占用。
【2】对象池管理:在对象池的实现中,使用 SoftReference 来保持对象的引用,以便在需要时快速获取对象。在内存紧张时,这些对象可以被回收,从而优化内存使用和性能。
【3】大型数据集处理:在处理大型数据集时,可以使用 SoftReference 存储中间结果。这样在内存不足时,可以让这些中间结果被回收,而不是占用大量内存,避免因内存溢出导致的崩溃。
【4】图形应用程序中的图像缓冲:在图形界面应用程序中,可以使用 SoftReference 来缓存图像。当内存不足时,图像可以被回收以释放内存,从而使应用能够继续运行而不至于崩溃。
【5】自定义数据结构:在自定义数据结构(如 LRU 缓存)中,可以使用 SoftReference 作为值来保存不常用的数据项。当内存紧张时,自动释放不再需要的数据,确保结构的内存占用保持在合理范围内。