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

Java提供了四种引用类型:强引用、软引用、弱引用和虚引用。虚引用,作为这四种引用类型中最特殊的一种,主要用于跟踪对象被垃圾回收的状态。

名词解释:
引用实例:软引用、弱引用、虚引用等引用实例。
引用对象:被引用实例指向的对象。

虚引用(Phantom Reference)

用途

虚引用这个对象,要不是我在研究Java中的引用规范的话,我都不知道有虚引用。这个对象比较偏门,如果不是写框架或者一些工具类的话,是根本用不到的。
虚引用是Java中最弱的一种引用类型。虚引用主要用于检测对象是否已经没有引用关系了(可被垃圾处理器回收)。
比如说,这个特性可以用来监测对象要被回收后,做一些资源回收的动作。
在JDK9,从内部的sun工具包中抽离了一个Cleaner工具类,该类就是用于对象被回收后,释放一些相关资源,避免资源占用以及浪费。该工具类中,正是利用了虚引用的特点来自动释放资源。

与其他引用的区别

软引用、弱引用都会在垃圾回收时,对引用对象进行垃圾回收,并且无法阻止。
但是在虚引用中,垃圾回收器会先将引用实例放进引用队列中,然后从引用队列中取出断开虚引用和引用对象的关系后才会被垃圾回收器回收。

引用类型垃圾回收时引用对象的状态
软引用只有在OOM抛出之前,才会回收引用对象
弱引用每次GC时,都会回收引用对象
虚引用不会GC,只会将引用实例放进引用队列中。需手动Clear后,引用对象才会被回收

如何创建和使用虚引用

虚引用需要与引用队列搭配使用,在虚引用中有且仅有一个构造方法:

public PhantomReference(T referent, ReferenceQueue<? super T> q) {
    super(referent, q);
}

虚引用与引用队列的关系

虚引用需搭配引用队列一起使用,在JVM中若是监测到引用对象要被回收,那么虚引用将会被添加到引用队列中。

引用队列(ReferenceQueue)

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

引用队列的作用和特点

特点:

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

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

  • 软引用、弱引用:无需手动出队,会在垃圾回收时清理引用对象。
  • 虚引用:需手动出队,然后将虚引用指向被引用对象的关系断开,说白了就是需要手动clear,不然该引用对象即使不可达了,但是虚拟机还是无法将其回收。

示例:结合引用队列监控对象生命周期

假设,你现在有一个这样的场景:
你有一个共享资源对象,会在多个线程中同时使用。但是你需要在使用完后,将占用的资源释放。
该怎么处理呢?这个时候你就不能使用完后,直接释放资源,因为有可能其他的线程正在使用或者即将使用。所以只能等该对象回收后,释放它占用的资源了。
此时,虚引用就体现出它的作用了。上面也说到了,虚引用能监听到对象的垃圾回收动作。所以我们能在准备回收时,关闭相关的资源。

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<>();

    /**
     * referent字段
     */
    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方法。

  • 12
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值