Hotspot 对象引用Reference和Finalizer 源码解析

目录

一、Reference

1、SoftReference / WeakReference / PhantomReference

2、定义

3、ReferenceHandler

4、Cleaner

二、ReferenceQueue

1、定义

2、  enqueue / reallyPoll

3、poll / remove 

4、forEach

三、Finalizer

1、定义

2、register

3、FinalizerThread

4、runFinalization / runAllFinalizers

5、InstanceKlass::register_finalizer


一、Reference

      Reference是所有表示对象引用的类的抽象基类,定义了公共的方法,Reference所引用的对象称为referent。因为Reference的实现与JVM的垃圾回收机制是强相关的,所以不建议直接继承Reference,避免改变Reference的关键实现,其类继承关系如下图:

下面逐一讲解各类的使用和源码实现细节。

1、SoftReference / WeakReference / PhantomReference

     这三个对象的表示引用是越来越弱的,SoftReference通常用来实现内存敏感的缓存,当内存不足的时候,为了获取可用内存空间会回收SoftReference所引用的对象,SoftReference本身会被放到创建时传入的ReferenceQueue中,JVM保证在抛出OutOfMemoryError异常前,清除所有的SoftReference。SoftReference增加了两个属性:

  • static long clock; //由垃圾回收器维护的表示时间的字段
  • long timestamp; //用来保存当前的clock

SoftReference改写了原来的构造方法和get方法的实现,增加了timestamp属性的更新逻辑,如下:

public SoftReference(T referent) {
        super(referent);
        this.timestamp = clock;
}

public SoftReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
        this.timestamp = clock;
    }

public T get() {
        T o = super.get();
        if (o != null && this.timestamp != clock)
            this.timestamp = clock;
        return o;
    }

      WeakReference通常用来实现类似WeakMap的特殊Map,不能阻止key或者value被垃圾回收了,当垃圾回收器发现一个对象只是被WeakReference所引用就会回收掉该对象,并将关联的WeakReference加入到创建时传入的ReferenceQueue中。WeakReference没有新增属性,只是定义了自己的构造方法而已,如下:

public WeakReference(T referent) {
        super(referent);
    }

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

     PhantomReference通常用来实现一种更流畅的类似Object.finalize的清理功能,与SoftReference和WeakReference不同的是,PhantomReference的get方法永远返回null,为了保证其所引用的对象一直处于可被回收的状态,并且当垃圾回收器判断某个对象只是被PhantomReference所引用,然后将PhantomReference加入到创建时传入的ReferenceQueue中,这个时候垃圾回收器不会立即回收掉PhantomReference所引用的对象,而是等到所有的PhantomReference对象都放到ReferenceQueue中或者PhantomReference对象本身变得不可达。WeakReference也没有新增属性,改写了原有的get方法实现,永远返回null,如下:

public T get() {
        return null;
    }

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

这三个类的代码调用都大同小异,具体可以参考《java8 WeakHashMap接口实现源码解析》中WeakReference的应用。

2、定义

   Reference包含的属性如下:

  • T referent; //所引用的对象
  • volatile ReferenceQueue<? super T> queue; //当所引用的对象referent被清理时用来保存Reference的队列,调用方可以通过ReferenceQueue的poll方法获取队列中的Reference实例,从而知道哪些referent对象被回收掉了
  • volatile Reference next; //ReferenceQueue使用,通过next属性将所有加入到ReferenceQueue中的Reference实例构成一个链表
  • transient Reference<T> discovered; //JVM使用的,用于将所有的Pending状态的Reference实例构成一个链表
  • static Lock lock = new Lock(); //用来修改Pending状态的Reference实例链表的锁
  • static Reference<Object> pending  = null; //Pending状态的Reference实例链表的链表头元素,垃圾回收器发现某个对象只有Reference实例引用,就会把Reference对象加入到这个链表中,而ReferenceHandler Thread不断从这个链表中移除元素,将其加入到Reference实例创建时传入的ReferenceQueue队列中

  Reference定义了四种内部状态:

  • Active 新创建的Reference实例就是Active状态,当垃圾回收器发现所引用的对象referent的可达状态发生变更了,可能将Reference实例的状态改成Pending或者Inactive,取决于Reference实例创建时是否传入ReferenceQueue实例引用。如果是从Active改成Pending,则垃圾回收器还会将该Reference实例加入到pending属性对应的Reference列表中
  • Pending  pending属性对应的Reference列表中的Reference实例的状态都是Pending,等待ReferenceHandler Thread将这些实例加入到queue队列中
  • Enqueued queue属性对应的ReferenceQueue队列中的Reference实例的状态都是Enqueued,当实例从ReferenceQueue队列中移除就会变成Inactive。如果Reference实例在创建时没有传入ReferenceQueue,则永远不会处于Enqueued状态。
  • Inactive 变成Inactive以后,状态就不会再变更,等待垃圾回收器回收掉该实例

 在不同的状态下,queue和next属性的赋值会发生变更,如下:

  • Active  queue就是Reference实例创建时传入的ReferenceQueue引用,如果没有传入或者传入的是null,则为ReferenceQueue.NULL;此时next属性为null。
  • Pending queue就是Reference实例创建时传入的ReferenceQueue引用,next属性是this
  • Enqueued  queue就是ReferenceQueue.ENQUEUED,next属性就是队列中的下一个元素,如果当前Reference实例就是最后一个,则是this
  • Inactive queue就是ReferenceQueue.NULL,next属性就是this

 Reference定义的方法比较简单,如下:

其中get方法返回所引用的对象referent,clear方法用于将referent置为null,enqueue方法用于将当前Reference实例加入到创建时传入的queue队列中,isEnqueued方法判断当前Reference实例是否已加入queue队列中,tryHandlePending方法是ReferenceHandler Thread调用的用于处理Pending状态的Reference实例的核心方法,是包级访问的。重点关注ReferenceHandler的实现。

3、ReferenceHandler

     ReferenceHandler继承自Thread,表示一个不断将Pending状态的Reference实例放入该实例创建时传入的ReferenceQueue实例中,所有处于Pending状态的Reference实例通过discovered实例属性构成链表,链表头就是Reference类的静态属性pending,在遍历链表时,如果链表为空则通过lock.wait()的方式等待;如果遍历的Reference实例是Cleaner,则调用其clean方法,用于清理资源清理。其实现如下:

private static class ReferenceHandler extends Thread {

        private static void ensureClassInitialized(Class<?> clazz) {
            try {
                Class.forName(clazz.getName(), true, clazz.getClassLoader());
            } catch (ClassNotFoundException e) {
                throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
            }
        }

        static {
            //预先加载并初始化两个类
            ensureClassInitialized(InterruptedException.class);
            ensureClassInitialized(Cleaner.class);
        }

        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }

        public void run() {
            while (true) {
                //while true不断执行
                tryHandlePending(true);
            }
        }
    }


static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            //注意lock和pending都是静态属性
            synchronized (lock) {
                if (pending != null) {
                    //如果存在待处理的Reference实例
                    r = pending
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值