美文网首页
深入分析Reference和ReferenceQueue

深入分析Reference和ReferenceQueue

作者: 码上述Andy | 来源:发表于2019-08-11 15:45 被阅读0次

    一.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<ReferenceBean> weakReference = new WeakReference<>(o,weakReferenceQueue);
    

    三.Reference状态

    源码:java.lang.ref.Reference

     volatile ReferenceQueue<? super T> 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<? super T> 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<T> 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<Object> 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<Object> 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<? super Object> 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<? 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.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

          本文链接:https://www.haomeiwen.com/subject/gwpedctx.html