JavaScript

Reference源码解析

2019-06-23  本文已影响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.
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共同完成:

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);
    }
上一篇 下一篇

猜你喜欢

热点阅读