【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方法。

相关推荐
_Evan_Yao7 小时前
Redis 缓存穿透、击穿、雪崩,一次讲清!
java·spring·mybatis
梓豪csd7 小时前
消息订阅中心:谁该收到哪条通知
java·开发语言·人工智能·低代码·自动化·1024程序员节
秋饼7 小时前
Spring AI 2.1 Message Parts 深度实战:推理签名回放与跨轮记忆持久化的源码级落地
java·ai·技术分享·后端开发
王中阳Go7 小时前
两个入口答案不一致先别调模型:6 行入口口径对账表(附脚本)
人工智能·后端·python·大语言模型·1024程序员节
thefool1122668 小时前
TCP 1
java·1024程序员节
码事漫谈9 小时前
消除AI幻觉,普通人能做的五件事
后端
金銀銅鐵10 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(下)
java·后端·ai编程
AI人工智能+电脑小能手11 小时前
一次请求打几个下游,这个Java并行任务工具类6个方法按序聚合
java·线程池·并发编程·1024程序员节·批量处理·并行任务
Mk5987812 小时前
给 Agent 装上对话框 —— AgentScope-Java 接入 Spring Boot 与 SSE 流式实战
后端
付威202312 小时前
从 Loop 到 Runtime:rpi Agent Loop 与 Cordis 的边界、取舍与组合
后端