谈谈Java Reference的原理

Reference

Reference是所有引用类型的父类,它与垃圾回收器合作来来进行GC,Reference类定义了子类的主要逻辑,所以在SoftReference、WeakReference和PhantomReference中几乎完全复用了Reference的逻辑。
Reference
下面进入Reference类中进行分析它的原理。

构造函数和属性

其中一个构造函数可以传入一个队列,如果不传入那么使用默认的队列。如果使用第一种方式的话,可以方便的通过队列的引用来判断对象是否被回收。

Reference(T referent) {
    this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
    this.referent = referent;
    this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}

ReferenceQueue队列实现了的入队(enqueue)和出队(poll还有remove)操作,内部元素就是泛型的Reference,通过Reference对象自身的链表结构所实现的。这一点后面再说。先看Reference的属性。

// 保存reference指向的对象。
private T referent;       
volatile ReferenceQueue<? super T> queue;
Reference next;
transient private Reference<T> discovered; 
private static Reference<Object> pending = null;
  • queue:引用对象关联的引用队列。是对象即将被回收时所要通知的队列。当对象将被回收时,reference对象( 而不是referent引用的对象 )会被放到queue里面,然后外部程序即可通过监控这个queue拿到相应的数据了。
    名义上是一个队列,实际内部是使用单链表来表示的单向队列,可以理解为queue就是一个链表,其自身仅存储当前的head节点,后面的节点由每个reference节点通过next来保持即可。
  • next:指向下一个引用,Reference是一个单链表的结构。
  • pending 等待添加到queue中的元素链表。注意这是一个静态对象,意味着所有Reference对象共用同一个pending链表。
  • discovered:表示要处理的对象的下一个对象。pending链表使用discovered来查找下一个reference,而ReferenceQueue则用next。

总结一下:

  1. 源码中没有给pending和discovered初始赋值的操作,那么必定是VM的操作。
  2. 有两个链表,分别是Reference Queue 和 pending list,两个链表的元素都是Reference的实例,分别通过next和discovered连接起来。前者的头元素在ReferenceQueue中,后者的头元素就是pending。
Reference对象的生命周期

在了解生命周期之前,我们先看一下pending list的处理线程,前面说到 VM 会自动的添加Reference对象到pending list中,另外一个单线程将pending list中的元素移到Reference Queue中。这个线程的定义以及启动在Reference类中。

private static class ReferenceHandler extends Thread {
    @Override
    public void run() {
        while (true) {
            tryHandlePending(true);
        }
    }
    static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    r = pending;// 拿到pending链表的头元素              
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    pending = r.discovered;//删除头元素,移向一下个Reference
                    r.discovered = null;
                } else {
                    if (waitForNotify) {
                        lock.wait();// notify唤醒操作在VM中
                    }
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            Thread.yield();
            return true;
        } catch (InterruptedException x) {
            return true;
        }
        if (c != null) {
            c.clean();
            return true;
        }
        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }
}

pending是由jvm来赋值的,当Reference内部的referent对象的可达状态改变时,jvm会将Reference对象放入pending链表。ReferenceHandler线程要做的是将pending对象enqueue,但默认我们所提供的queue,也就是从构造函数传入的是null,实际是使用了ReferenceQueue.NULL,Handler线程判断queue为ReferenceQueue.NULL则不进行操作,只有非ReferenceQueue.NULL的queue才会将Reference进行enqueue。
结合代码已经源码的中的大幅注释, Reference的生命周期如下图 :

在这里插入图片描述
Reference实例一共有四种状态,Active(活跃状态)、Pending(半死不活状态)、Enqueued(濒死状态)、Inactive(死亡状态),

  • Active:
    reference如果处于此状态,会受到垃圾处理器的特殊处理。当垃圾回收器检测到referent已经更改为合适的状态后(没有任何强引用和软引用关联)
  • Pending:
    实例如果处于此状态,表明它是pending-Reference列表中的一个元素,等待被Reference-handler线程做入队处理。
  • Enqueued:
    实例如果处于此状态,表明它已经是它注册的引用队列中的一个元素,当它被从引用队列中移除时,它的状态将会变为Inactive,未注册引用队列的实例永远不会处于该状态。
  • Inactive:
    实例如果处于此状态,它的状态将永远不会再改变了。

JVM并没有明确的定义状态,而是通过现有的字段值来体现Reference的状态。

  • next==null,则reference处于Active状态;
  • next!=null && queue == ReferenceQueue.NULL, 则reference处于Inactive状态;
  • next!=null && queue == ReferenceQueue.ENQUEUED,则reference处于Enqueue状态;
  • discovered!=null 则reference处于Pending状态。
小结
  1. Reference框架是作为 JVM GC于语言层面的一个消息传递方式,使我们可以对对象被回收时做一些处理。比如WeakHashMap DirectByteBuffer正是利用此机制来实现。
  2. 构造Reference及其子类时,若传入了ReferenceQueue需要及时Poll,否则Reference对象不能得到释放
  3. 本文大体的介绍了Reference框架,没有描述其子类的具体流程,后面会逐个介绍。
  4. 如果在创建Reference时没有传入Queue,那么Reference对象也不会进入Pedding list,从hander方法中也可以看出。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值