深入分析Reference和ReferenceQueue

一.Reference类型(4种类型引用)

1.StrongReference(强引用):垃圾回收器不会回收释放该对象,需要自己去释放。当内存不足时就直接抛出OOM。
2.SofeReference(软引用):当内存不足时,触发GC会释放对象内存,内存足够即使GC也不会释放该对象。
3.WeakReferecne(弱引用):当触发GC时就会释放对象内存。

  1. PhantomReference(虚引用):具体也不知道用来干嘛。

二.WeakReference和ReferenceQueue关系

引用队列在垃圾回收器回收对象的时候,会将已经注册的对象添加到引用队列中。我们可以通过引用队列判断该对象是否已经释放掉。上篇分析的LeakCanary原理就是利用这点。
当需要检测某个对象是否被回收,我们可以将Reference关联到ReferenceQueue上,具体如下:

ReferenceBean o = new ReferenceBean();
ReferenceQueue weakReferenceQueue = new ReferenceQueue();
WeakReference weakReference = new WeakReference<>(o,weakReferenceQueue);

三.Reference状态

源码:java.lang.ref.Reference

 volatile ReferenceQueue queue;

    /* When active:   NULL
     *     pending:   this
     *    Enqueued:   next reference in queue (or this if last)
     *    Inactive:   this
     */
    @SuppressWarnings("rawtypes")
    Reference next;

active:内存被分配的时候状态。
pending:即将回收内存,存入关联的引用queue中时的状态。
Enqueued:内存被回收的时候,进入引用队列中的状态。
Inactive:最终不活跃状态,该状态不会转换成其他状态。

四.Reference与Referencequeue源码分析

1.Reference
源码:java.lang.ref.Reference
我们首先看看有哪些成员对象

 private T referent;         /* Treated specially by GC */

    volatile ReferenceQueue queue;

    /* When active:   NULL
     *     pending:   this
     *    Enqueued:   next reference in queue (or this if last)
     *    Inactive:   this
     */
    @SuppressWarnings("rawtypes")
    Reference next;

    /* When active:   next element in a discovered reference list maintained by GC (or this if last)
     *     pending:   next element in the pending list (or null if last)
     *   otherwise:   NULL
     */
    transient private Reference discovered;  /* used by VM */


    /* Object used to synchronize with the garbage collector.  The collector
     * must acquire this lock at the beginning of each collection cycle.  It is
     * therefore critical that any code holding this lock complete as quickly
     * as possible, allocate no new objects, and avoid calling user code.
     */
    static private class Lock { }
    private static Lock lock = new Lock();


    /* List of References waiting to be enqueued.  The collector adds
     * References to this list, while the Reference-handler thread removes
     * them.  This list is protected by the above lock object. The
     * list uses the discovered field to link its elements.
     */
    private static Reference pending = null;
 
 

referent:表示引用对象。
queue:关联的引用队列。
next:下一个即将被处理的节点。
discovered:要处理的下一个对象。
pending:等待被入队列的引用列表。

当Reference被加载的时候,就好执行Reference类中的静态代码块,所以我们先从Reference类静态代码块开始分析:

static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        /* If there were a special system-only priority greater than
         * MAX_PRIORITY, it would be used here
         */
        handler.setPriority(Thread.MAX_PRIORITY);
        handler.setDaemon(true);
        handler.start();

        // provide access in SharedSecrets
        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            @Override
            public boolean tryHandlePendingReference() {
                return tryHandlePending(false);
            }
        });
    }

静态代码块中其实启动了一个线程,然后priority优先级为:MAX_PRIORITY,然后设置是否是后台驻留线程,启动。
然后我们接着分析ReferenceHandler这个线程:

 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 {
            // pre-load and initialize InterruptedException and Cleaner classes
            // so that we don't get into trouble later in the run loop if there's
            // memory shortage while loading/initializing them lazily.
            ensureClassInitialized(InterruptedException.class);
            ensureClassInitialized(Cleaner.class);
        }

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

        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
    }

看run方法,一个死循环,我们再跟进tryHandlePending方法。

static boolean tryHandlePending(boolean waitForNotify) {
        Reference r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    r = pending;
                    // 'instanceof' might throw OutOfMemoryError sometimes
                    // so do this before un-linking 'r' from the 'pending' chain...
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    // unlink 'r' from 'pending' chain
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    // The waiting on the lock may cause an OutOfMemoryError
                    // because it may try to allocate exception objects.
                    if (waitForNotify) {
                        lock.wait();
                    }
                    // retry if waited
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            // Give other threads CPU time so they hopefully drop some live references
            // and GC reclaims some space.
            // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above
            // persistently throws OOME for some time...
            Thread.yield();
            // retry
            return true;
        } catch (InterruptedException x) {
            // retry
            return true;
        }

        // Fast path for cleaners
        if (c != null) {
            c.clean();
            return true;
        }

        ReferenceQueue q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }
 
 

当pending不为null的时候,penging上面提到过是等待被入队列的引用列表,看最后两行代码就是加入到引用队列中。当pending为null时会一直处于wait状态。
那WeakReference和ReferenceQueue是怎样关联的呢,看最后一行代码if (q != ReferenceQueue.NULL) q.enqueue(r)
接下来我们分析下ReferenceQueue中enqueue方法:
源码:java.lang.ref.ReferenceQueue

boolean enqueue(Reference 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.queue = ENQUEUED;
            r.next = (head == null) ? r : head;
            head = r;
            queueLength++;
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(1);
            }
            lock.notifyAll();
            return true;
        }
    }

首先判断queue是否为空或者是否为ENQUEUED状态,如果是直接返回。
如果当queue不为空时,将queue置为ENQUEUED状态,表示已经回收。
然后将Reference r插入到队列的头部。
至此Reference和ReferenceQueue源码就分析完了。

That's ALL

你可能感兴趣的:(深入分析Reference和ReferenceQueue)