Java Reference源码分析

来源:互联网 发布:域名污染查询 编辑:程序博客网 时间:2024/05/29 08:47

Reference是个抽象类,它实现了所有reference对象的通用操作。这个类主要跟垃圾收集器密切相关。

Reference内部四种状态

  1. Active:Reference对象被垃圾收集器特殊对待。当垃圾收集器探测到引用可达性后,它将改变reference对象到pending状态或者iactive状态。如果refence对象在创建的时候包含一个queue时,它将改变对象的状态到pending。如果没有queue,它直接将对象改变到iactive状态。新创建的reference对象是active状态。
  2. Pending: 一个refer对象在pending-reference集合中,等待reference线程处理器加入到队列中。如果对象在创建的时候没有注入队列,则引用对象不会有此状态。
  3. Enqueued:当一个refer对象加入到队列后,就变为enqueued状态。
  4. Inactive:不能发生任何状态的转化

总结:当垃圾收集回收引用对象时,如果对象在创建的时候注入了队列,则把回收的对象加入到队列。加入队列的目的是为了程序员手动删除被垃圾回收的对象。为什么还要手动删除被回收的引用对象了?我们看一下WeakHashMap的源码实现

  /**     * The entries in this hash table extend WeakReference, using its main ref     * field as the key.     */    private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {        V value;        final int hash;        Entry<K,V> next;        /**         * Creates new entry.         */        Entry(Object key, V value,              ReferenceQueue<Object> queue,              int hash, Entry<K,V> next) {            super(key, queue);            this.value = value;            this.hash  = hash;            this.next  = next;        }

从上面的源码中可以看出,在创建Entry的时候注入了ReferenceQueue队列。由于Entry继承了WeakReference,但是并没有包装成WeakReference。
那么为什么不把Entry直接包装成WeakReference? 原因很简单,加入把Entry包装成WeakReference,就不能使用Hash的功能,依据Key定位value。

通过在Entry的构造函数中调用super(key)。把key包装成WeakReference对象,当垃圾收集器判定到只有引用可达性时,就把key设置为null。程序员通过迭代queue中的元素,把table中的key为null的元素删除

Reference内部的实例字段及类字段

     /* 被GC处理 */    private T referent;    //引用队列    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状态:是discovered 引用集合中下一个元素通过GC维护     * pending: 是pending list 中的下一个元素     * otherwise:   NULL     */    //被vm使用    transient private Reference<T> discovered;  /* used by VM */     /*等待进入队列的集合列表。垃圾收集器添加引用到这个集合,等待Reference-Handler线程移除它们。     *此字段是个类常量。所有的实例对象都有一个pending.     */    private static Reference<Object> pending = null;

上面源码中queue与next字段的关系

  • Active:queue = ReferenceQueue当queue被注册时。或者queue=ReferenceQueue.NULL。next = null;
  • Pending:queue=ReferenceQueue当queue被注册时。next=this
  • Enqueued: queue = ReferenceQueue.ENQUEUED;next=队列的下一个实例。或者如过是队列的尾部,则是队列本身。
  • Inactive: queue = ReferenceQueue.NULL; next = this.

添加pending引用元素到queue源码分析

当一个引用的队列不为空时,在进行垃圾回收的时候把状态转化为pending状态。
经过前面的介绍,当状态转变为pending状态时,discovered中保存的是pending list中的下一个元素。
把r从pending list中移除。加入到queue
pending = r.discovered;
r.discovered = null;

   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;    }
原创粉丝点击