Java提供了四种引用类型:强引用、软引用、弱引用和虚引用。虚引用,作为这四种引用类型中最特殊的一种,主要用于跟踪对象被垃圾回收的状态。
名词解释:
引用实例:软引用、弱引用、虚引用等引用实例。
引用对象:被引用实例指向的对象。
虚引用(Phantom Reference)
用途
虚引用这个对象,要不是我在研究Java中的引用规范 的话,我都不知道有虚引用 。这个对象比较偏门,如果不是写框架或者一些工具类的话,是根本用不到的。
虚引用是Java中最弱的一种引用类型。虚引用主要用于检测对象是否已经没有引用关系了(可被垃圾处理器回收)。
比如说,这个特性可以用来监测对象要被回收后,做一些资源回收的动作。
在JDK9,从内部的sun
工具包中抽离了一个Cleaner
工具类,该类就是用于对象被回收后,释放一些相关资源,避免资源占用以及浪费。该工具类中,正是利用了虚引用的特点来自动释放资源。
与其他引用的区别
软引用、弱引用都会在垃圾回收时,对引用对象进行垃圾回收,并且无法阻止。
但是在虚引用中,垃圾回收器会先将引用实例放进引用队列 中,然后从引用队列中取出 ,断开虚引用和引用对象的关系后才会被垃圾回收器回收。
引用类型 | 垃圾回收时引用对象的状态 |
---|---|
软引用 | 只有在OOM抛出之前,才会回收引用对象 |
弱引用 | 每次GC时,都会回收引用对象 |
虚引用 | 不会GC,只会将引用实例放进引用队列中。需手动Clear后,引用对象才会被回收 |
如何创建和使用虚引用
虚引用需要与引用队列搭配使用,在虚引用中有且仅有一个构造方法:
java
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
虚引用与引用队列的关系
虚引用需搭配引用队列一起使用,在JVM中若是监测到引用对象要被回收,那么虚引用将会被添加到引用队列中。
引用队列(ReferenceQueue)
引用队列是用来配合引用工作的,不同类型的引用在对象被垃圾回收前,会有不同的行为。
引用队列的作用和特点
特点:
- 当引用对象已经不可达了并且虚拟机准备垃圾回收时,就会将其入队。
- 整个入队过程基于虚拟机且异步,无需等待。
根据引用的不同,进入引用队列后引用对象的状态也不一致
- 软引用、弱引用:无需手动出队,会在垃圾回收时清理引用对象。
- 虚引用 :需手动出队,然后将虚引用指向被引用对象的关系断开,说白了就是需要手动clear,不然该引用对象即使不可达了,但是虚拟机还是无法将其回收。
示例:结合引用队列监控对象生命周期
假设,你现在有一个这样的场景:
你有一个共享资源对象,会在多个线程中同时使用。但是你需要在使用完后,将占用的资源释放。
该怎么处理呢?这个时候你就不能使用完后,直接释放资源,因为有可能其他的线程正在使用或者即将使用。所以只能等该对象回收后,释放它占用的资源了。
此时,虚引用就体现出它的作用了。上面也说到了,虚引用能监听到对象的垃圾回收动作。所以我们能在准备回收时,关闭相关的资源。
java
package com.azir.reference;
import java.io.Closeable;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class AutoCloseUtil {
/**
* 引用队列,用于存放被回收的虚引用
*/
public static final ReferenceQueue<Object> REFERENCE_QUEUE = new ReferenceQueue<>();
private static final List<PhantomImpl<? extends Closeable>> PHANTOM_LIST = new ArrayList<>();
private static final Field REFERENT;
/*
* 增加自动释放资源动作
*/
public static void addAutoClose(Object object) {
if (object instanceof Closeable) {
PHANTOM_LIST.add(new PhantomImpl<>((Closeable) object));
}
}
static {
try {
REFERENT = Reference.class.getDeclaredField("referent");
REFERENT.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
new Thread(() -> {
while (true) {
try {
PhantomImpl<?> phantom = (PhantomImpl<?>) REFERENCE_QUEUE.remove();
if (phantom == null) {
continue;
}
Object o = REFERENT.get(phantom);
((Closeable) o).close();
System.out.println("资源成功关闭");
} catch (Exception e) {
System.out.println("清理资源时错误");
}
}
}, "auto-close-resource").start();
}
public static class PhantomImpl<T extends Closeable> extends PhantomReference<T> {
public PhantomImpl(T referent) {
super(referent, REFERENCE_QUEUE);
}
}
}
在上面的这个工具类中,有一个静态方法addAutoClose
。该方法用于自动释放对象所占用的资源。目前该工具类仅支持继承了Closeable
类的对象。
主要的逻辑是新开了一个线程,在线程中循环地从引用队列中获取虚引用,根据反射从虚引用中获取引用对象,从而调用引用对象的Close方法。