Java中的强引用、弱引用、软引用和虚引用
Java中的强引用、弱引用、软引用和虚引用
当我们用Java编程时,我们经常使用硬引用,通常甚至不会考虑它——这是一个很好的理由,因为它们是大多数情况下的最佳选择。然而,有时我们需要对垃圾回收器清理对象的时间有更多的控制。
在本文中,我们将探讨硬引用和各种非硬引用类型之间的区别,以及我们何时可以使用它们。
2. 硬引用
硬引用(或强引用)是默认类型的引用,我们大多数时候甚至不会考虑引用对象何时以及如何被垃圾回收。假设我们创建了一个_ArrayList_对象并将其分配给_list_变量:
List```<String>``` list = new ArrayList<>();
垃圾回收器不能回收这个列表,因为我们在_list_变量中持有它的强引用。但是,如果我们将变量置为null:
list = null;
现在,只有现在,_ArrayList_对象才能被回收,因为没有任何引用持有它。
3. 超出硬引用
硬引用是默认的有一个很好的理由。它们让垃圾回收器按预期工作,所以我们不必担心管理内存分配。尽管如此,有些情况下我们希望即使仍然持有这些对象的引用,也要收集对象并释放内存。
软引用告诉垃圾回收器,引用的对象可以在回收器的自由裁量下被回收。该对象可以在内存中停留一段时间,直到回收器决定需要回收它。这将特别发生在JVM面临内存耗尽风险时。所有仅通过软引用可到达的对象的软引用应在抛出_OutOfMemoryError_异常之前被清除。
我们可以通过将对象包装在软引用中来轻松使用软引用:
SoftReference<List```<String>```> listReference = new SoftReference<>(new ArrayList```<String>```());
如果我们想检索引用对象,我们可以使用_get_方法。由于对象可能已经被清除,我们需要检查它:
List```<String>``` list = listReference.get();
if (list == null) {
// 对象已经被清除
}
4.1. 使用案例
软引用可以用来使我们的代码对与内存不足相关的错误更加有弹性。例如,我们可以创建一个对内存敏感的缓存,当内存稀缺时自动驱逐对象。我们不需要手动管理内存,因为垃圾回收器会为我们做。
5. 弱引用
仅由弱引用引用的对象不会被阻止被回收。 从垃圾回收的角度来看,它们可能根本不存在。如果一个弱引用的对象应该被保护免于被清除,它也应该被一些硬引用所引用。
5.1. 使用案例
弱引用最常用于创建规范化映射。这些映射只映射可以到达的对象。一个很好的例子是_WeakHashMap_,它的工作原理与普通的_HashMap_相同,但它的键是弱引用的,当引用对象被清除时,它们会自动被移除。
使用_WeakHashMap_,我们可以创建一个短暂存在的缓存,清除不再被代码的其他部分使用的对对象。如果我们使用普通的_HashMap_,仅仅因为键在映射中的存在就会阻止它被垃圾回收器清除。
6. 虚引用
与弱引用类似,虚引用不会阻止垃圾回收器将对象排队等待清除。不同之处在于虚引用必须从引用队列中手动轮询后才能被最终确定。这意味着我们可以在它们被清除之前决定我们想做什么。
6.1. 使用案例
如果我们需要实现一些最终确定逻辑,虚引用是很好的选择,它们比_finalize_方法更可靠和灵活。让我们编写一个简单的方法,它将遍历引用队列并对所有引用执行清理:
private static void clearReferences(ReferenceQueue queue) {
while (true) {
Reference reference = queue.poll();
if (reference == null) {
break; // 没有引用要清除
}
cleanup(reference);
}
}
虚引用不允许我们使用_get_方法检索它们的引用对象。由于这个原因,通常的做法是扩展_PhantomReference_类,以包含对我们的清理逻辑重要的信息。
虚引用的其他重要用例是调试和内存泄漏检测。即使我们不需要执行任何最终确定操作,我们可以使用虚引用来观察哪些对象正在被分配和何时。
7. 结论
在本文中,我们探讨了硬引用和不同类型的非硬引用及其用例。我们了解到软引用可以用于内存保护,弱引用用于规范化映射,虚引用用于细粒度的最终确定。