美文网首页JavaScript
Reference源码解析

Reference源码解析

作者: 王侦 | 来源:发表于2019-06-23 15:26 被阅读14次
    Abstract base class for reference objects.  This class defines the
    operations common to all reference objects.  Because reference objects are
    implemented in close cooperation with the garbage collector, this class may
    not be subclassed directly.
    

    引用对象的抽象基类。该类定义了所有引用对象的共同操作。因为引用对象的实现与GC紧密相关,所以该类最好不要直接被子类化。

    1.引用实例的四种状态

    A Reference instance is in one of four possible internal states:
    
        Active: Subject to special treatment by the garbage collector.  Some
        time after the collector detects that the reachability of the
        referent has changed to the appropriate state, it changes the
        instance's state to either Pending or Inactive, depending upon
        whether or not the instance was registered with a queue when it was
        created.  In the former case it also adds the instance to the
        pending-Reference list.  Newly-created instances are Active.
    
        Pending: An element of the pending-Reference list, waiting to be
        enqueued by the Reference-handler thread.  Unregistered instances
        are never in this state.
    
        Enqueued: An element of the queue with which the instance was
        registered when it was created.  When an instance is removed from
        its ReferenceQueue, it is made Inactive.  Unregistered instances are
        never in this state.
    
        Inactive: Nothing more to do.  Once an instance becomes Inactive its
        state will never change again.
    
    • Active状态:当GC检测到引用可达性已经改变,其会根据实例在创建时是否注册queue而将引用的状态改为Pending或者Inactive。对于Pending状态,会将引用实例加入到pending-Reference链表。新建的实例状态是Active。(GC需要特殊处理)
    • Pending状态:等待Reference-handler线程将元素加入pending-Reference链表,标识的就是这种元素。没有注册(队列)的元素不会处于这种状态。
    • Enqueued状态:实例创建时注册的队列中的元素。当实例从ReferenceQueue移除时,变为Inactive状态。没有注册的实例不会处于该状态。
    • Inactive状态:终结状态。
    The state is encoded in the queue and next fields as follows:
    
        Active: queue = ReferenceQueue with which instance is registered, or
        ReferenceQueue.NULL if it was not registered with a queue; next =
        null.
    
        Pending: queue = ReferenceQueue with which instance is registered;
        next = this
    
        Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance
        in queue, or this if at end of list.
    
        Inactive: queue = ReferenceQueue.NULL; next = this.
    
    With this scheme the collector need only examine the next field in order
    to determine whether a Reference instance requires special treatment: If
    the next field is null then the instance is active; if it is non-null,
    then the collector should treat the instance normally.
    
    To ensure that a concurrent collector can discover active Reference
    objects without interfering with application threads that may apply
    the enqueue() method to those objects, collectors should link
    discovered objects through the discovered field. The discovered
    field is also used for linking Reference objects in the pending list.
    

    状态的编码由域queue和next共同完成:

    • Active状态
      queue = ReferenceQueue 实例创建时注册;ReferenceQueue.NULL未注册;
      next = null
    • Pengding状态
      queue = ReferenceQueue 实例创建时注册
      next = this
    • Enqueued状态
      queue = ReferenceQueue.ENQUEUEd
      next = queue中下一个实例,若是链表list末尾,则是this
    • Inactive状态
      queue = ReferenceQueue.NULL
      next = this

    GC只需要检查next域就可以决定引用实例是否需要特殊处理,如果next不为null,则无需特殊处理。

    为了确保并发GC能够发现active引用对象并可能对这些对象调用enqueue方法而不会干扰应用线程,GC需要通过discovered域来链接discovered objects。discovered域也用来在pending list中链接Reference对象。

    2.域

    queue队列使用next来查找下一个reference,pending队列使用discovered来查找下一个reference。

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

    reference指代引用本身,referent指代reference引用的对象。

        volatile ReferenceQueue<? super T> queue;
    
        /* When active:   NULL
         *     pending:   this
         *    Enqueued:   next reference in queue (or this if last)
         *    Inactive:   this
         */
        @SuppressWarnings("rawtypes")
        volatile 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();
    

    GC每次回收时都必须首先拿到该锁。所以任何获取该锁的代码必须尽快完成,不能分配任何对象并且避免调用用户代码。

        /* 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;
    

    需要被入队的References链表。当Reference-handler线程移除Referencs时,GC将其加入到该链表中。该链表被上面的lock对象保护。该链表使用discovered域来链接其元素。

    3.构造函数

        Reference(T referent) {
            this(referent, null);
        }
    

    未注册ReferenceQueue,只有Active和InActive两种状态。

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

    4.Reference Handler线程

        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);
                }
            });
        }
    

    Reference Handler线程会注册到根线程组并设置最高优先级。

        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);
                }
            }
        }
    
        /**
         * Try handle pending {@link Reference} if there is one.<p>
         * Return {@code true} as a hint that there might be another
         * {@link Reference} pending or {@code false} when there are no more pending
         * {@link Reference}s at the moment and the program can do some other
         * useful work instead of looping.
         *
         * @param waitForNotify if {@code true} and there was no pending
         *                      {@link Reference}, wait until notified from VM
         *                      or interrupted; if {@code false}, return immediately
         *                      when there is no pending {@link Reference}.
         * @return {@code true} if there was a {@link Reference} pending and it
         *         was processed, or we waited for notification and either got it
         *         or thread was interrupted before being notified;
         *         {@code false} otherwise.
         */
        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 Reference。如果可能有另外一个pending Reference返回true,若没有则返回false并线程可以做些其他工作而不是循环。

    waitForNotify:为true并且没有任何pending Reference,则等待直到VM通知或者中断;为false,如果没有pending Referenc则立即返回。

    返回值:true如果有Reference pending并且被处理了,或者等待通知时,要么获得了通知,要么被中断了。

    该方法的核心流程:从pending链表移除头结点(并将pending更新为pending.discovered也即下一个结点),如果该结点是Cleaner,则执行clean操作,否则将该结点入到其注册的ReferenceQueue中。

    5.核心方法

        /**
         * Returns this reference object's referent.  If this reference object has
         * been cleared, either by the program or by the garbage collector, then
         * this method returns <code>null</code>.
         *
         * @return   The object to which this reference refers, or
         *           <code>null</code> if this reference object has been cleared
         */
        public T get() {
            return this.referent;
        }
    
        /**
         * Clears this reference object.  Invoking this method will not cause this
         * object to be enqueued.
         *
         * <p> This method is invoked only by Java code; when the garbage collector
         * clears references it does so directly, without invoking this method.
         */
        public void clear() {
            this.referent = null;
        }
    

    调用该方法不会是的对象进入Enqueued状态。

    
        /* -- Queue operations -- */
    
        /**
         * Tells whether or not this reference object has been enqueued, either by
         * the program or by the garbage collector.  If this reference object was
         * not registered with a queue when it was created, then this method will
         * always return <code>false</code>.
         *
         * @return   <code>true</code> if and only if this reference object has
         *           been enqueued
         */
        public boolean isEnqueued() {
            return (this.queue == ReferenceQueue.ENQUEUED);
        }
    
    
        /**
         * Adds this reference object to the queue with which it is registered,
         * if any.
         *
         * <p> This method is invoked only by Java code; when the garbage collector
         * enqueues references it does so directly, without invoking this method.
         *
         * @return   <code>true</code> if this reference object was successfully
         *           enqueued; <code>false</code> if it was already enqueued or if
         *           it was not registered with a queue when it was created
         */
        public boolean enqueue() {
            return this.queue.enqueue(this);
        }
    

    相关文章

      网友评论

        本文标题:Reference源码解析

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