前言
在 Java 中,引用随处可见,我们通过类似 Object obj = new Object();
的代码就可以创建一个引用,而我们直接通过这个代码段创建的引用被称为强引用(StrongReference
),这种引用的特点是其指向的对象无论如何都不会被 JVM 的垃圾回收器(Garbage Collector
)回收(即使是面临着发生 OutOfMemoryError
异常的风险)。
但是可能在开发中,我们可能会需要一些具有其他特性的引用对象,比如说:我们需要某种引用可以提供这种功能:在新建其他对象时,如果当前堆内存足够用来分配给要新建的对象时,那么垃圾回收器不会回收这种引用指向的对象,但是如果当前可分配的堆内存不足时,我们希望垃圾回收器可以回收这种引用指向的对象,以提供足够的内存来创建新的对象。
另外三种引用
而在 JDK 1.2 之后,Java 提供了除了强引用之外的另外三种引用,他们分别是:软引用(SoftReference)、弱引用(WeakReference)、虚引用(PhantomReference)
。通过他们我们可以实现我们上面所说的应用场景。下面一起来看一下这 3 中引用。
软引用
这种引用的功能即是前言部分提到的:如果某个 Java 对象只被软引用所指向,那么在 JVM 要新建一个对象的时候,如果当前虚拟机所剩下的堆内存不足以保存这个要新建的对象的时候(即虚拟机将要抛出 OutOfMemoryError
异常的时候),那么 JVM 会发起一次垃圾回收(gc)动作,将堆中所 只被非强引用 指向的对象回收,以提供更多的可用内存来新建这个对象,如果经过垃圾回收动作之后虚拟机的堆内存中仍然没有足够的可用空间来创建这个对象,那么虚拟机将抛出一个 OutOfMemoryError
异常。在 Java 1.2 之后,提供了 SoftReference
类来表示软引用。
弱引用
和软引用类似,但是弱引用的引用强度更弱一点,上文说到:只被软引用指向的对象会在 JVM 在新建对象并且其可用的堆内存不足以保存这个对象时会被垃圾回收器回收。即当 JVM 在新建一个对象并且第一次面临着抛出 OutOfMemoryError
异常时会被垃圾回收器回收。
而对于只被弱引用指向的对象来说,其只能存活到下一次 JVM 执行垃圾回收动作之前。也就是说:JVM 的每一次垃圾回收动作都会回收那些只被弱引用指向的对象。在 Java 1.2 之后,提供了 WeakReference
类来表示弱引用。
虚引用
引用强度最弱的引用,这种引用有点特殊:被虚引用完全不会引用其所指向的对象的生命周期,也就是说一个 Java 对象是否被回收和指向它的虚引用完全没有任何关系。也不能通过虚引用来得到其指向的对象(其 get
方法直接返回 null)。那么虚引用有什么作用呢?虚引用一般会配合 引用队列(ReferenceQueue)
来使用。当某个被虚引用指向的对象被回收时,我们可以在其引用队列中得到这个虚引用的对象作为其所指向的对象被回收的一个通知。我们将会在后面看到这种用法。
实验代码
好了,理论说完了,下面我们来实践一下:我们新建一个 Java 工程,在里面新建一个类 Main
并添加实践代码(请谨慎尝试):
public class Main {
// 引用测试类
static class ReferenceTest {
static final int _1M = 1024;
// 引用队列,当某个引用所指向的对象被回收时这个引用本身会被添加到其对应的引用队列中
// 其泛型为其中存放的引用要指向的对象类型
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 强引用测试
void testStrongReference() {
ArrayList<byte[]> strongReferences = new ArrayList<>();
try {
while (true) {
strongReferences.add(new byte[_1M]);
}
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
}
// 软引用测试
void testSoftReference() {
ArrayList<SoftReference> softReferences = new ArrayList<>();
try {
while (true) {
softReferences.add(new SoftReference<>(new byte[_1M], referenceQueue));
}
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
}
// 弱引用测试
void testWeakReference() {
ArrayList<WeakReference> weakReferences = new ArrayList<>();
try {
while (true