【Java引用规范】虚引用以及引用队列

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)

引用队列是用来配合引用工作的,不同类型的引用在对象被垃圾回收前,会有不同的行为。

引用队列的作用和特点

特点:

  1. 当引用对象已经不可达了并且虚拟机准备垃圾回收时,就会将其入队。
  2. 整个入队过程基于虚拟机且异步,无需等待。

根据引用的不同,进入引用队列后引用对象的状态也不一致

  • 软引用、弱引用:无需手动出队,会在垃圾回收时清理引用对象。
  • 虚引用 :需手动出队,然后将虚引用指向被引用对象的关系断开,说白了就是需要手动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方法。

相关推荐
lybugproducer1 小时前
创建型设计模式之:简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式
java·设计模式·建造者模式·简单工厂模式·工厂方法模式·抽象工厂模式·面向对象
南客先生1 小时前
马架构的Netty、MQTT、CoAP面试之旅
java·mqtt·面试·netty·coap
Minyy111 小时前
SpringBoot程序的创建以及特点,配置文件,LogBack记录日志,配置过滤器、拦截器、全局异常
xml·java·spring boot·后端·spring·mybatis·logback
百锦再1 小时前
Java与Kotlin在Android开发中的全面对比分析
android·java·google·kotlin·app·效率·趋势
武昌库里写JAVA2 小时前
39.剖析无处不在的数据结构
java·vue.js·spring boot·课程设计·宠物管理
画个大饼3 小时前
Go语言实战:快速搭建完整的用户认证系统
开发语言·后端·golang
Nelson_hehe5 小时前
Java基础第四章、面向对象
java·语法基础·面向对象程序设计
Thomas_YXQ5 小时前
Unity3D Lua集成技术指南
java·开发语言·驱动开发·junit·全文检索·lua·unity3d
ShiinaMashirol6 小时前
代码随想录打卡|Day27(合并区间、单调递增的数字、监控二叉树)
java·算法
东阳马生架构7 小时前
Nacos简介—3.Nacos的配置简介
java