Reference

Reference


1. Summary

1.jdk 1.8.0

2. Reference level
  • FinalReference , not provided for external use, the access permission of the class is the default protect, even if an out of memory exception is thrown, the occupied memory will not be reclaimed
  • SoftReference , when memory is not enough, reclaim memory space before throwing out of memory
  • WeakReference , the first GC is not recycled, when the second GC is recycled, it will be in memory until the second GC
  • PhantomReference


3. Citation comparison
- strong citation soft reference weak reference phantom reference
definition Strong reference means that there is a directly accessible reference in the program without any reference object, such as Object obj = new Object(); In, obj is a strong reference Soft references, not strong references, but can be accessed through soft reference objects. For soft referenced objects, only when memory is insufficient (before an OOM exception is thrown), the garbage collector will decide to reclaim the object pointed to by the soft reference. Soft references are often used to implement memory-sensitive caches. Weak references, non-strong references and soft references, but can be accessed through weak reference objects. Weakly referenced objects, regardless of whether there is enough memory, will be reclaimed as long as they are found by the garbage collector. For practical applications, see WeakHashMap A virtual reference, which must be used together with the reference queue (ReferenceQueue), is generally used to track the collection action of the garbage collector. For example, when the object is recycled, the finalize method of the object will be called. This can be achieved by using virtual references. action, and more safety


4. Other
  • Subclasses cannot be directly inherited; for security reasons, the Reference class interacts with the GC; implementation method: the access permission of the constructor is the default, that is, the classes outside the package are not visible
  • Objects encapsulate references to other objects and can operate like ordinary objects. Under certain restrictions, they support interaction with the garbage collector. That is, you can use the Reference object to refer to other objects, but they will eventually be reclaimed by the garbage collector. Programs also sometimes need to be notified after an object has been reclaimed to inform that the reachability of the object has changed


5. Process




2. Source code analysis


/**
 * 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.
 *
 * @author   Mark Reinhold
 * @since    1.2
 */

public abstract class Reference<T> {


Annotation for the class:
  • The Reference class is the abstract base class for reference objects
  • Common operations for referencing objects are defined in the Reference class
  • Since reference objects are implemented by working closely with the garbage collector, you cannot directly subclass this class


Implementations of the Reference class that cannot be inherited by classes outside the package:
  • The access permission of the constructor of the Reference class is the default, that is, the classes in the same package are visible
  • The constructor of the subclass calls the constructor of the parent class through super() by default, and cannot access the constructor of the parent class

    // constructor, two
    // Difference: with or without ReferenceQueue parameter
    // Constructor without ReferenceQueue parameter, when the Reference is reclaimed by GC, it directly changes from Active state to InActive state
    
    Reference(T referent) {
        this(referent, null);
    }

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

    // The construction method with the ReferenceQueue parameter, when the Reference is reclaimed by the GC, it will be added to the GC and automatically added to the pending-Reference list, that is, the attribute pengding in the class  
    private static Reference<Object> pending = null;

    // Corresponding to the two parameters passed in by the constructor
    private T referent;         /* Treated specially by GC */
    volatile ReferenceQueue<? super T> queue;



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


  • Use: The access permission is the default, and this attribute is not used in the current class; it is used for calling other classes in the same package, such as: ReferenceQueue used in
  • Function: The Reference data structure is a linked list, which is used to connect subsequent objects
  • Reference next , in ReferenceQueue Refers to the ReferenceQueue in the parameters passed in by the constructor


Reference state The pointer to next in ReferenceQueue
Active NULL
Pending itself
Enqueued the next element or itself (the trailing element points to itself)
Inactive NULL


/* 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.
     */
    // Used to save the reference of the object, GC will treat it according to different Reference, the parameters of the constructor
    private T referent;         /* Treated specially by GC */
    
    // Brief description: If a notification mechanism is required, save the corresponding queue
    // use: parameters of the constructor
    // Function: When creating a Reference, register the Queue in the Reference, when the object referenced by the Reference
    // When recycled by the garbage collector, the Reference will be placed in the queue, which is equivalent to a notification mechanism
    volatile ReferenceQueue<? super T> queue;

    /* 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
     */
    // Point to the next object in the queue; different from next, used in the current class
    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.
     */
    // A collection of objects waiting to be enqueued;
    // When the Reference-handler thread deletes the element, the GC adds the deleted element to this queue;
    // This collection is thread-safe through the lock above
    // This collection links its own elements via the discovered property
    // Elements in pending queue are automatically added by GC (objects are put into this queue after recycling)
    private static Reference<Object> pending = null;


It is known from the attributes in the class: the internal data result is

the role of the singly linked list ReferenceQueue<? super T> queue
  • The queue is passed in through the constructor. If there is no default, it is null. It is used to store the reference object registered on the queue.
  • Queue distinguishes Reference in different states, and different states correspond to different queues



Reference has 4 states
  • Active: References in the Active state will receive special attention from the GC. When the GC perceives that the reachability of the reference has changed to another state, its state will change to Pending or Inactive. Whether it is converted to Pending or Inactive depends on this. Whether the queue is registered when the Reference object is created. If the queue is registered, this instance will be added to the pending-Reference list. The state of the newly created Reference instance is Active.
  • Pending: The elements in the pending-Reference list that are waiting to be queued by the Reference-handler thread are in this state. An instance without a registered queue will never reach this state
  • Enqueued: The state of the object in the queue. When the instance is moved out of the ReferenceQueue, the state of the Reference is Inactive. It is impossible to reach this state without a registered ReferenceQueue
  • Inactive: Once an instance becomes Inactive, this state will never be changed again


The queue corresponding to the four states of Reference
  • Active queue: There is no object injection in the queue or queue just created; next = null; it can be seen from the construction method that newly created instances are in this state
  • Pending queue:
  • Enqueued queue:
  • Inactive queue:


    /* High-priority thread to enqueue pending References
     */
    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; // subclass of PhantomReference
        try {
            synchronized (lock) {
                // pengding Reference Queue is not empty
                if (pending != null) {
                    r = pending; // assign pending to r
                    // 'instanceof' might throw OutOfMemoryError sometimes
                    // so do this before un-linking 'r' from the 'pending' chain...
                    // Is r an instance object of Cleaner
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    // unlink 'r' from 'pending' chain
                    // pendding points to the next element
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    // pending Reference Queue is empty
                    // 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;
    }





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



As can be seen from the source code, this thread is started in the static building block of the Reference class and is set to the highest priority and daemon state. What this thread has to do is to constantly check whether the pending is null. If the pending is not null, the pending will be enqueue, otherwise the thread will be in the wait state.

    /**
     * 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
     */
    // Returns the object pointed to by the currently referenced object; returns NULL if the object has been cleared or reclaimed by the GC
    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.
     */
    // Triggering this method will not enqueue the operation
    public void clear() {
        this.referent = null;
    }


    /* -- 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
     */
    // Determine if the 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
     */
    // enqueue operation
    public boolean enqueue() {
        return this.queue.enqueue(this);
    }



Blog reference:
"Java Source Code Analysis": ReferenceQueue, Reference and its subclasses
Java Reference source code analysis

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326541442&siteId=291194637