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;
                    //Cleaner是PhantomReference的子类
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    //通过discovered属性将所有Pending的Reference实例构成一个链表
                    //获取下一个处于Pending的Reference实例
                    pending = r.discovered;
                    //discovered属性置为空
                    r.discovered = null;
                } else {
                    //waitForNotify默认为true,阻塞当前线程直到其他线程唤醒
                    if (waitForNotify) {
                        lock.wait();
                    }
                    // retry if waited
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            //yield方法会让出当前线程的CPU处理时间,让垃圾回收线程获取更多的CPU时间,加速垃圾回收
            Thread.yield();
            return true;
        } catch (InterruptedException x) {
            // retry
            return true;
        }

        if (c != null) {
            //pending属性是Cleaner,执行清理
            c.clean();
            return true;
        }

        //pending属性不是Cleaner
        //Pending状态下,r.queue就是最初r创建时传入的ReferenceQueue引用
        ReferenceQueue<? super Object> q = r.queue;
        //将r加入到queue 队列中
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }

  ReferenceHandler的启动是通过静态static块完成的,如下:

static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        //往上遍历找到最初的父线程组
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        //创建ReferenceHandler线程,并启动     
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        handler.setPriority(Thread.MAX_PRIORITY); //最高优先级
        handler.setDaemon(true); //后台线程
        handler.start();

        //允许访问SharedSecrets
        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            @Override
            public boolean tryHandlePendingReference() {
                return tryHandlePending(false);
            }
        });
    }

4、Cleaner

     Cleaner继承自PhantomReference,其源码可以参考OpenJDK jdk\src\share\classes\sun\misc\Cleaner.java。Cleaner表示一种更轻量更健壮的资源清理方式,相对于Object的finalization机制,轻量是因为Cleaner不是JVM创建的,不需要借助JNI调用创建,执行资源清理的代码是ReferenceHandler Thread调用的而非finalizer Thread;健壮是因为Cleaner继承自PhantomReference,是最弱的一种引用类型,可以避免恶心的顺序问题。Cleaner封装了执行资源清理任务的逻辑,具体的资源清理逻辑通过创建Cleaner时的方法入参Runnable方法指定,Cleaner保证执行资源清理任务是线程安全的,即会捕获所有的异常,且保证只执行一次。一旦垃圾回收器发现Cleaner实例是phantom-reachable,即没有其他实例强引用该实例,垃圾回收器就会把Cleaner实例加入到Reference的pending队列中,由ReferenceHandler Thread负责调用其clean方法执行资源清理动作。Cleaner并不能完全替代Object的finalization机制,使用Cleaner时要求其资源清理逻辑比较简单,否则容易阻塞ReferenceHandler Thread,阻塞其他的资源清理任务执行。其实现如下:

public class Cleaner
    extends PhantomReference<Object>
{

    // 因为PhantomReference的构造方法要求必须传入ReferenceQueue参数,所以这里声明了一个,但是实际上并不会往里面添加Cleaner实例
    //因为ReferenceHandler Thread会直接调用Cleaner实例的clean方法,不会将其加入到dummyQueue队列中
    private static final ReferenceQueue<Object> dummyQueue = new ReferenceQueue<>();

    //Cleaner链表的链表头
    static private Cleaner first = null;

    private Cleaner
        next = null,
        prev = null;

    //add方法将cl加入到链表的头部
    private static synchronized Cleaner add(Cleaner cl) {
        if (first != null) {
            cl.next = first;
            first.prev = cl;
        }
        first = cl;
        return cl;
    }

    //remove方法将cl从链表中移除
    private static synchronized boolean remove(Cleaner cl) {

        //说明cl已经从链表移除了,不需要再处理
        if (cl.next == cl)
            return false;

        // Update list
        if (first == cl) {
            if (cl.next != null)
                first = cl.next;
            else
                first = cl.prev;
        }
        if (cl.next != null)
            cl.next.prev = cl.prev;
        if (cl.prev != null)
            cl.prev.next = cl.next;

        //从链表移除后会将cl的prev和next都指向它自己
        cl.next = cl;
        cl.prev = cl;
        return true;

    }

    private final Runnable thunk;

    private Cleaner(Object referent, Runnable thunk) {
        super(referent, dummyQueue);
        this.thunk = thunk;
    }

    /**
     * 核心入口方法,创建Cleaner,thunk就是具体的执行资源清理的逻辑
     */
    public static Cleaner create(Object ob, Runnable thunk) {
        if (thunk == null)
            return null;
        return add(new Cleaner(ob, thunk));
    }

    /**
     * Runs this cleaner, if it has not been run before.
     */
    public void clean() {
        //首先从队列移除当前Cleaner实例
        if (!remove(this))
            return;
        try {
            //执行资源清理
            thunk.run();
        } catch (final Throwable x) {
            //捕获所有异常,打印err日志并退出
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        if (System.err != null)
                            new Error("Cleaner terminated abnormally", x)
                                .printStackTrace();
                        System.exit(1);
                        return null;
                    }});
        }
    }

}

Cleaner通过prev,next属性内部维护了一个双向链表,其中静态属性first就是链表头,即所有的Cleaner实例通过链表维护着引用关系,但是这种引用是phantom-reachable的,一旦某个Cleaner实例没有强引用则会被垃圾回收器加入到Reference的pending队列中,等待被处理。Cleaner的核心方法有两个,一是创建Cleaner实例的create方法,该方法会将创建的实例加入到链表中;另外一个是执行资源清理的clean方法,该方法将当前实例从链表中移除,然后执行Cleaner实例创建时传入的thunk,如果出现异常则打印err日志并导致JVM进程终止。

可以参考java.nio.DirectByteBuffer类中Cleaner的应用,如下:

cleaner是该类的一个私有属性,在构造函数中初始化,当DirectByteBuffer实例被垃圾回收器回收掉了,这个cleaner属性对应的Cleaner实例就没有其他强引用了,只剩下Cleaner内部维护的链表对其的虚引用了,就会被垃圾回收器加入到Reference的pending队列中,等待被处理。

二、ReferenceQueue

1、定义

     ReferenceQueue主要用来通知Reference实例的使用方Reference实例对应的referent对象已经被回收掉了,允许使用方对Reference实例本身做适当的处理。注意ReferenceQueue本身并不直接持有Reference实例的引用,如果Reference实例本身变得不可达了,则无论Reference实例对应的referent对象被回收掉了,Reference实例都不会被添加到ReferenceQueue中。

ReferenceQueue包含的属性如下:

  • static ReferenceQueue<Object> NULL = new Null<>();  //如果Reference实例的queue等于NULL,则表示该实例已经从队列中移除
  • static ReferenceQueue<Object> ENQUEUED = new Null<>(); // //如果Reference实例的queue等于ENQUEUED,则表示该实例已经加入到队列中
  • Lock lock = new Lock(); //改写队列的锁
  • volatile Reference<? extends T> head = null;  //Reference链表的头部
  • long queueLength = 0; //表示Reference链表的长度

上面的Null 和Lock都是ReferenceQueue的内部类,如下:

private static class Null<S> extends ReferenceQueue<S> {
        boolean enqueue(Reference<? extends S> r) {
            return false;
        }
    }
 
static private class Lock { };

 ReferenceQueue跟正常的队列实现不同,ReferenceQueue依赖于Reference的next属性构成一个链表,链表头就是ReferenceQueue的静态head属性,加入到队列中实际就是插入到链表的头部。当Reference实例加入到ReferenceQueue中,Reference实例变成新的链表头,next属性就指向原来的链表头,queue属性变成ENQUEUED,相关逻辑在enqueue方法中;当Reference实例从ReferenceQueue中移除时,next属性被重置为自己,原来的next属性变成新的链表头,queue属性变成NULL,相关逻辑在reallyPoll方法中。重点关注以下方法的实现。

2、  enqueue / reallyPoll

       enqueue方法将Reference实例加入到链表的头部,reallyPoll方法移除并返回链表的头部,这两方法都要求获取lock属性的锁,其中enqueue是public方法,reallyPoll方法是private方法。具体实现如下:

//enqueue方法将某个Reference实例加入到队列中
    boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
        synchronized (lock) {
            // Check that since getting the lock this reference hasn't already been
            // enqueued (and even then removed)
            ReferenceQueue<?> queue = r.queue;
            if ((queue == NULL) || (queue == ENQUEUED)) {
                return false;
            }
            assert queue == this;
            //将r插入到链表的头部,r的queue置为ENQUEUED,表示其已经加入到队列中,r的next属性置为它自己
            r.queue = ENQUEUED;
            //插入第一个元素时,head等于null,此时r的next属性就是r,插入以后的元素时,next属性就是head
            r.next = (head == null) ? r : head;
            head = r;
            queueLength++;
            if (r instanceof FinalReference) {
                //增加FinalReference的计数
                sun.misc.VM.addFinalRefCount(1);
            }
            //唤醒其他的等待线程
            lock.notifyAll();
            return true;
        }
    }

    private Reference<? extends T> reallyPoll() {       /* Must hold lock */
        Reference<? extends T> r = head;
        if (r != null) {
            @SuppressWarnings("unchecked")
            Reference<? extends T> rn = r.next;
            //将链表头从链表中移除,移除的Reference实例的queue会被置为NULL,next置为它自己
            head = (rn == r) ? null : rn;
            r.queue = NULL;
            r.next = r;
            queueLength--;
            if (r instanceof FinalReference) {
                //FinalReference的计数器减1
                sun.misc.VM.addFinalRefCount(-1);
            }
            return r;
        }
        return null;
    }

3、poll / remove 

     这两方法都是移除并返回链表的头元素,区别在于poll方法不会阻塞,立即返回null,remove方法会阻塞当前线程,直到当前线程获取了一个Reference实例或者累计等待时间超过了指定时间,remove方法还有一个没有参数的重载版本,会阻塞当前线程,直到当前线程被唤醒,等待时间无限制,如果被唤醒了链表头还是null则返回null,即只等待一次。其实现如下:

 /**
     *  从当前队列中移除并返回链表头元素,如果为空则返回null,WeakHashMap中就是调用此方法遍历队列中所有的Reference实例
     */
    public Reference<? extends T> poll() {
        if (head == null)
            //队列为空,返回null
            return null;
        synchronized (lock) {
            //获取队列的锁,移除并返回链表头元素
            return reallyPoll();
        }
    }

    /**
     * 移除并返回队列的头元素,最多等待timeout,如果timeout等于0,则只等待一次直到线程被唤醒,等待时间无限制
     */
    public Reference<? extends T> remove(long timeout)
        throws IllegalArgumentException, InterruptedException
    {
        if (timeout < 0) {
            throw new IllegalArgumentException("Negative timeout value");
        }
        synchronized (lock) {
            Reference<? extends T> r = reallyPoll();
            if (r != null) return r;
            long start = (timeout == 0) ? 0 : System.nanoTime();
            //相当于while死循环
            for (;;) {
                //等待最多timeout,如果timeout为0,则等待其他线程调用notify或者notifyAll
                lock.wait(timeout);
                //移除并返回链表头部元素
                r = reallyPoll();
                if (r != null) return r;
                if (timeout != 0) {
                    //检查是否等待超时
                    long end = System.nanoTime();
                    //如果timeout为0,则start为0,timeout算出来的就是一个负值,会立即返回null,即只wait一次
                    //如果timeout不为0,则可能wait多次,直到多次wait的累计时间大于设定的值,返回null
                    timeout -= (end - start) / 1000_000;
                    if (timeout <= 0) return null;
                    //继续下一次wait
                    start = end;
                }
            }
        }
    }

    /**
     * remove(long timeout)的重载版本,timeout固定传0
     */
    public Reference<? extends T> remove() throws InterruptedException {
        return remove(0);
    }

4、forEach

      forEach方法用于遍历链表中的所有的Reference实例,通常用于调试目的,要求调用方不能保持对Reference实例的引用,避免影响其正常销毁,其实现如下:

/**
       用来遍历队列中所有Reference实例,通常用于调试目的,要求调用方不能保持对Reference实例的引用,避免影响其正常销毁
     */
    void forEach(Consumer<? super Reference<? extends T>> action) {
        //注意执行forEach时不要求获取锁,所以读取的元素可能已经从队列中移除了
        for (Reference<? extends T> r = head; r != null;) {
            action.accept(r);
            @SuppressWarnings("unchecked")
            Reference<? extends T> rn = r.next;
            if (rn == r) {
                if (r.queue == ENQUEUED) {
                    //说明已经遍历到队列最后一个元素,将r置为null
                    r = null;
                } else {
                    //r.queue等于NULL,说明r已经从队列中移除了,需要从head开始重新遍历,以后r后面多个元素都可能被移除了,而且
                    //此时也无法获取下一个遍历元素的引用
                    r = head;
                }
            } else {
                // next in chain
                r = rn;
            }
        }
    }

三、Finalizer

1、定义

       Finalizer继承自FinalReference,FinalReference是一种特殊的引用类型,主要用来辅助实现Object finalization机制,其定义如下:

Finalizer定义的属性如下:

  • static ReferenceQueue<Object> queue = new ReferenceQueue<>(); //全局的ReferenceQueue队列
  • static Finalizer unfinalized = null; //所有Finalizer 实例构成的链表的头元素
  • static final Object lock = new Object(); //修改链表的锁
  • private Finalizer next = null, prev = null; //用来构成链表的表示下一个和上一个元素

     Finalizer是借助垃圾回收器对Reference实例的特殊处理机制实现的,每创建一个实现了finalize方法的对象时,JVM会通过调用Finalizer的register方法创建一个新的Finalizer实例,该对象就是Finalizer实例的referent对象,所有的Finalizer实例构成一个链表。当某个对象只被Finalizer实例所引用,则将对应的Finalizer实例加入到Reference维护的pending链表中,通过ReferenceHandler Thread将pending链表中的Finalizer实例加入到Finalizer定义的全局ReferenceQueue中。Finalizer自身会另外起一个新线程,FinalizerThread,不断的从全局的ReferenceQueue中取出带出来的Finalizer实例,然后将该实例从Finalizer链表中移除,最后调用对应对象的finalize方法执行资源的清理,并将对referent对象的引用置为null,保证该对象能够会回收掉。当JVM进程即将退出,JVM会通过java.lang.Runtime另起线程处理掉全局ReferenceQueue中未处理完的Finalizer实例,通过java.lang.Shutdown另起线程处理掉Finalizer链表中的Finalizer实例,即没有加入到Reference维护的pending链表中的Finalizer实例。重点关注以下方法的实现。

2、register

      register是JVM创建对象时,如果该类实现了finalize方法,则以新创建的对象作为参数调用此方法创建一个Finalizer实例,并将其加入到Finalizer链表的头部,其实现如下:

 /*在对象创建的时候由JVM调用 */
static void register(Object finalizee) {
        new Finalizer(finalizee);
    }

private Finalizer(Object finalizee) {
        super(finalizee, queue);
        //执行构造方法的时候,会将当前实例加入到链表中
        add();
 }

private void add() {
        synchronized (lock) {
            //获取锁,将this插入到链表的头部
            if (unfinalized != null) {
                this.next = unfinalized;
                unfinalized.prev = this;
            }
            unfinalized = this;
        }
    }

3、FinalizerThread

      FinalizerThread就是一个不断循环的线程任务,从queue属性中获取待处理的Finalizer实例,将该实例从Finalizer链表中移除然后调用其finalize方法,最后将Finalizer实例对referent对象的引用置为null,从而保证GC能够正确回收该对象,其实现如下:

private static class FinalizerThread extends Thread {
        //标记运行状态
        private volatile boolean running;
        
        FinalizerThread(ThreadGroup g) {
            super(g, "Finalizer");
        }
        public void run() {
            //已运行
            if (running)
                return;

            //isBooted返回false表示JVM未初始化完成
            while (!VM.isBooted()) {
                try {
                    //等待JVM初始化完成
                    VM.awaitBooted();
                } catch (InterruptedException x) {
                    // ignore and continue
                }
            }
            final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
            running = true;
            for (;;) {
                try {
                    //移除并返回链表头元素,如果为空则等待
                    Finalizer f = (Finalizer)queue.remove();
                    //执行Finalizer任务
                    f.runFinalizer(jla);
                } catch (InterruptedException x) {
                    // ignore and continue
                }
            }
        }
    }

private void runFinalizer(JavaLangAccess jla) {
        synchronized (this) {
            //hasBeenFinalized返回true,说明该元素已经从队列移除了,直接返回
            if (hasBeenFinalized()) return;
            //将当前实例从队列中移除
            remove();
        }
        try {
            //获取所引用的对象referent
            Object finalizee = this.get();
            if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
                //实际调用Object的finalize方法
                jla.invokeFinalize(finalizee);
                /* 去掉对finalizee的引用,让GC回收掉该实例 */
                finalizee = null;
            }
        } catch (Throwable x) { }
        //将所引用的对象referent置为null,即去掉对referent的引用,让GC回收掉该实例
        super.clear();
    }

private boolean hasBeenFinalized() {
        //next等于this说明该实例已经从链表中移除了,已经执行过Finalized方法了
        return (next == this);
    }


private void remove() {
        synchronized (lock) {
           //获取锁,将this从链表中移除
            if (unfinalized == this) {
                if (this.next != null) {
                    unfinalized = this.next;
                } else {
                    unfinalized = this.prev;
                }
            }
            if (this.next != null) {
                this.next.prev = this.prev;
            }
            if (this.prev != null) {
                this.prev.next = this.next;
            }
            //将next和prev都指向自己,表示已经从链表中移除
            this.next = this;   /* Indicates that this has been finalized */
            this.prev = this;
        }
    }

FinalizerThread是通过静态static块启动的,如下:

static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread finalizer = new FinalizerThread(tg);
        finalizer.setPriority(Thread.MAX_PRIORITY - 2);
        finalizer.setDaemon(true);
        finalizer.start();
    }

 上述jla.invokeFinalize(finalizee)方法的实现如下:

可以搜索SharedSecrets.setJavaLangAccess方法的调用链找到上述实现,如下:

 4、runFinalization / runAllFinalizers

      runFinalization由Runtime.runFinalization()方法调用,负责清理掉queue中未处理的Finalizer实例;runAllFinalizers由java.lang.Shutdown,负责清理掉Finalizer链表中剩余的即未加入到queue中的Finalizer实例;两者都是调用forkSecondaryFinalizer方法执行清理任务,该方法会在系统线程组下另起一个线程执行指定任务,并等待该线程执行完成,如果执行异常,则终止当前线程。其实现如下:

 /* Called by Runtime.runFinalization(),执行queue中未处理的Finalizer */
    static void runFinalization() {
        if (!VM.isBooted()) {
            return;
        }

        forkSecondaryFinalizer(new Runnable() {
            private volatile boolean running;
            public void run() {
                // in case of recursive call to run()
                if (running)
                    return;
                final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
                running = true;
                for (;;) {
                    //不断遍历queue中所有的Finalizer,然后执行finalize方法
                    Finalizer f = (Finalizer)queue.poll();
                    if (f == null) break;
                    f.runFinalizer(jla);
                }
            }
        });
    }

    /* Invoked by java.lang.Shutdown,执行未加入到queue中的Finalizer */
    static void runAllFinalizers() {
        if (!VM.isBooted()) {
            return;
        }

        forkSecondaryFinalizer(new Runnable() {
            private volatile boolean running;
            public void run() {
                // in case of recursive call to run()
                if (running)
                    return;
                final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
                running = true;
                for (;;) {
                    //不断遍历unfinalized链表中的元素,执行finalize方法
                    Finalizer f;
                    synchronized (lock) {
                        f = unfinalized;
                        if (f == null) break;
                        unfinalized = f.next;
                    }
                    f.runFinalizer(jla);
                }}});
    }

/* 
       在系统线程组下创建一个新的线程执行指定任务,并等待任务执行完成,之所以开启一个新的线程,是为了与已经死锁了或者停顿的finalizer thread隔离开来
       加速finalize方法的执行
     */
    private static void forkSecondaryFinalizer(final Runnable proc) {
        AccessController.doPrivileged(
            new PrivilegedAction<Void>() {
                public Void run() {
                    ThreadGroup tg = Thread.currentThread().getThreadGroup();
                    //从当前线程的线程组往上遍历找到最初的系统线程组
                    for (ThreadGroup tgn = tg;
                         tgn != null;
                         tg = tgn, tgn = tg.getParent());
                    //启动一个新线程执行proc任务    
                    Thread sft = new Thread(tg, proc, "Secondary finalizer");
                    sft.start();
                    try {
                        //等待proc任务执行完成
                        sft.join();
                    } catch (InterruptedException x) {
                         //执行异常,中断当前线程
                        Thread.currentThread().interrupt();
                    }
                    return null;
                }});
    }

5、InstanceKlass::register_finalizer

      该方法就是JVM中调用Finalizer的register方法的具体实现了,如下:

instanceOop InstanceKlass::register_finalizer(instanceOop i, TRAPS) {
  if (TraceFinalizerRegistration) {
    //打印跟踪日志
    tty->print("Registered ");
    i->print_value_on(tty);
    tty->print_cr(" (" INTPTR_FORMAT ") as finalizable", (address)i);
  }
  //i是新创建的对象
  instanceHandle h_i(THREAD, i);
  //result表示调用结果
  JavaValue result(T_VOID);
  //args表示方法参数
  JavaCallArguments args(h_i);
  //获取调用方法
  methodHandle mh (THREAD, Universe::finalizer_register_method());
  //执行方法调用
  JavaCalls::call(&result, mh, &args, CHECK_NULL);
  return h_i();
}

static Method*      finalizer_register_method()     { return _finalizer_register_cache->get_method(); }

  _finalizer_register_cache的初始化在Universe的universe_post_init方法中,如下:

详细代码说明可以参考《Hotspot 内存管理之Universe 源码解析》 

register_finalizer方法的调用链如下:

InstanceKlass::allocate_instance就是根据Class创建对象的底层实现,可以参考《Hotspot Java对象创建和TLAB源码解析》;另外几个Runtime结尾的是给字节码的汇编指令或者编译器的编译代码使用的方法,最终的调用场景一样是创建对象。 

发布了117 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_31865983/article/details/103979571