1.7、JDK 源码分析 - Reference框架(一)

原创
2022/07/05 18:24
阅读数 339

摘要

上一篇博文讲解了ThreadLocal相关的知识,其中线程本地变量ThreadLocalMap中的每一项其实是封装的一个Entry对象,Entry继承了WeakReference(弱引用),而ThreadLocalMap每一项的key是作为弱引用传入了WeakReference进行构造,value保存在Entry新扩展的字段中;源码如下:

        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }


谈到引用,这里面学问可就非常大;因为它与JVM虚拟机的垃圾回收息息相关;下面就带大家来深入了解JDK的引用框架的奥秘;

** JVM垃圾回收-引用相关知识回顾**

对象的访问定位

虚拟机建立对象后,java程序需要通过栈上的reference数据来操作对上的具体对象。由于reference可续在Java虚拟机规范中只规定了一个指针对象的引用,并没有具体定义这个引用应该通过何种方式去定位、访问对重的对象的具体位置,所以对象访问方式也是取决于各个虚拟机自己的实现。目前主流的访问方式有使用句柄和直接指针两种。

1)、如果使用句柄访问的话,那么Java对中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息

2)、如果使用直接指针访问,那么Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象地址.

这两种对象访问方式各有优势,使用句柄访问最大的好处是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改。

使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。对于虚拟机Sun HotSpot而言,它使用的是直接指针访问方式,但是整个软件开发访问来看,各种语言和框架使用句柄来访问的情况也非常常见。

到这里,我们在来看看Thread中本地变量在虚拟机中的一个引用关系图,就更加清晰;

判断对象是否可回收

垃圾收集器在回收对象前,需要判断哪些对象已经‘’死去‘’(即不可能在被任何途径使用的对象);

引用计数算法

给对象中添加一个引用计算器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。但是,在Java虚拟机里面没有选用引用计数器算法来管理内存,其中最主要的原因是它很难解决对象之间的相互循环引用问题。

可达性分析算法

在主流的商用应用程序语言(Java、C#等)的主流实现中,都是称通过可达性分析来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连接(GC Roots到这个对象不可达)时,则证明此对象是不可用的。

在Java语言中,可作为GC Roots对象包括下面几种:

1)、虚拟机栈(栈帧中的本地变量表)中引用的对象.

2)、方法区中类静态属性引用的对象.

3)、方法区中常量引用的对象

4)、本地方法栈中JNI(即一般说的Native方法)引用的对象。

再谈引用

通过前面的知识回顾,相信大家已经了解到,判断对象是否存活的关键就是“引用”;在JDK1.2之前,Java中的引用定义:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就显得无能为力。我们希望描述这样一类对象:当内存空间还足够时,则暴露在内存之中;如果内存空间在进行垃圾收集后还是非常紧张,则可也抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。

在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为了强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4种,这4种引用强度依次逐渐减弱。

1)、强引用就是指在程序代码中普遍存在的,类似"Object obj=new Object()"这类的引用,只要引用还存在,垃圾回收器永远不会回收掉被引用的对象。

2)、软引用是用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK1.2之后提供了SoftRefrence类用来实现软引用。

3)、弱引用也是用来描述非必需对象的,但是它的强度比软引用还要更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用管理的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。

4)、虚引用也成为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能再这个对象被收集器回收时收到一个系统通知。在JDK1.2之后,提供了PhantomReference类来实现虚引用。

引用框架类UML关系

一、Reference类

Reference类比较简单,总共只有300行代码;从JDK源码类注释我们可以了解的信息如下: 该类是引用对象的抽象基类。 该类定义了所有引用对象通用的操作。 因为引用对象是与垃圾收集器紧密合作实现的,所以这个类不能直接子类化。

一个Reference实例处于四种可能的内部状态之一:

Active(活动): 由垃圾收集器进行特殊处理。 在收集器检测到referent的可达性已改变到适当的状态一段时间后,它将实例的状态更改为Pending或Inactive,这取决于创建实例时是否向队列注册了实例。 在前一种情况下,它还将实例添加到pending-Reference列表。新创建的实例状态为“激活”。

Pending(待处理): Pending - reference列表的一个元素,等待被Reference-handler线程加入队列。 未注册的实例永远不会处于这种状态。

Enqueued(排队的):创建实例时注册的队列中的一个元素。 当一个实例从它的ReferenceQueue中移除时,它被设置为Inactive。 未注册的实例永远不会处于这种状态。

Inactive(失活的): 不会做其它了。 一旦实例变为Inactive,它的状态将不会再改变。

Reference核心处理流程:

Reference状态流

成员变量和构造函数

    private T referent;         /* GC专门处理 */
    volatile ReferenceQueue<? super T> queue;
    Reference next;
    transient private Reference<T> discovered;  /* used by VM */
    /*
     *对象,用于与垃圾收集器同步。 收集器必须在每次收集周期开始时获取此锁。 因此,至关重要的是,任何持有此锁的代码都要尽快完成,不分配新对象,        *并避免调用用户代码
     */
    static private class Lock { }
    private static Lock lock = new Lock();
    private static Reference<Object> pending = null;
    Reference(T referent) {
        this(referent, null);
    }
    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }

说明

referent:Reference引用包装的原对象

queue:引用队列,用于存放处于Enqueued排队中的引用对象;

next:引用单向链表;在对象处于不同状态时,该值代表不同的含义;

状态在队列和next字段中编码如下:

  • Active: 注册队列的实例 queue = ReferenceQueue. 如果没有注册队列的实例,则为queue=ReferenceQueue.NULL; next=null
  • Pending: 注册队列的实例 queue = ReferenceQueue ; next = this
  • Enqueued: queue = ReferenceQueue.ENQUEUED; next =在队列中的下一个实例,或者this(如果在列表末尾)。
  • Inactive: queue = ReferenceQueue.NULL; next = this.

在这种模式下,收集器只需要检查下一个字段,以确定一个Reference实例是否需要特殊处理:如果下一个字段为空,那么实例是活动的; 如果它是非空的,那么收集器应该正常处理实例。

discovered:单向链表,由 JVM 维护;在 GC 标记的时候,当引用强弱关系达到一定条件,GCRoot可达性发生改变时,由 JVM 添加;该字段在不同状态下也有不同的含义;

  • active: GC维护的discovered引用列表中的下一个元素 (如果是最后一个,则为this)
  • pending: 待处理列表中的下一个元素 (如果是最后一个,则为null )
  • otherwise: NULL

pending:等待加入队列的引用列表。 收集器将引用添加到此列表,而引用处理程序线程将删除它们。 该列表受上述锁对象的保护。 列表使用发现的字段链接其元素。

构造函数:可以看到Reference类有两个构造函数,参数主要有引用包装的原对象referent和注册队列RQ(ReferenceQueue)两个;从定义上看,我们在包装引用对象的时候,可以不传入RQ(即不注册ReferenceQueue);

类加载初始化阶段

    static {
        //获取当前线程的线程组
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        //如果当前线程组循环去获取最顶层的父线程组
        for (ThreadGroup tgn = tg; tgn != null; tg = tgn, tgn = tg.getParent());
        //创建一个引用处理器线程,并且加入当前线程的最顶层父线程组
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        /*
         *设置最大优先级
         */
        handler.setPriority(Thread.MAX_PRIORITY);
        //设置为守护线程
        handler.setDaemon(true);
        handler.start();

        // 在 SharedSecrets中提供访问
        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            @Override
            public boolean tryHandlePendingReference() {
                return tryHandlePending(false);
            }
        });
    }
public interface JavaLangRefAccess {
    boolean tryHandlePendingReference();
}

说明:

Reference在类加载的初始化阶段调用<clinit>类构造方法执行静态代码块,在静态代码块中,创建了一个最高优先级且线程组为系统线程组的ReferenceHandler守护线程用于处理将pending状态的引用放入队到RQ中;

SharedSecrets.setJavaLangRefAccess这一段封装了一个JavaLangRefAccess对象,该对象用于包装引用对象的tryHandlePending方法。其它地方可以调用SharedSecrets.getJavaLangRefAccess该方法,然后执行。更具体一点就是,JDK中Bits类reserveMemory()方法中会调用。reserveMemory()在分配或释放直接内存时,应该调用这些方法。 它们允许用户控制进程可以访问的直接内存的数量。 所有大小都以字节为单位指定。

    static void reserveMemory(long size, int cap) {

        if (!memoryLimitSet && VM.isBooted()) {
            maxMemory = VM.maxDirectMemory();
            memoryLimitSet = true;
        }

        // optimist!
        if (tryReserveMemory(size, cap)) {
            return;
        }

        final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();

        // retry while helping enqueue pending Reference objects
        // which includes executing pending Cleaner(s) which includes
        // Cleaner(s) that free direct buffer memory
        while (jlra.tryHandlePendingReference()) {
            if (tryReserveMemory(size, cap)) {
                return;
            }
        }

        // trigger VM's Reference processing
        System.gc();

上面这段代码: while (jlra.tryHandlePendingReference()) ---循环尝试入队pending状态的引用对象, 引用对象,其中包括执行pending状态的Cleaner,Cleaner中包括释放直接缓冲内存的一些Cleaner ;

需要说明的是Cleaner可以用来实现对堆外内存进行管理,DirectByteBuffer就是通过Cleaner实现堆外内存回收的:基本原理是创建Cleaner的时候会传入堆外内存对应的引用以及清理内存相关的runnable实现,一旦该引用被回收,则会触发Cleaner相关机制并执行传入的runnable实现中的清理逻辑。

    // Primary constructor
    //
    DirectByteBuffer(int cap) {                   // package-private

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }

Reference的内部类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 {
            // 预加载并初始化InterruptedException和Cleaner类,这样我们在惰性加载/初始化它们时,如果内存不足,就不会在运行循环中遇到麻烦。
            ensureClassInitialized(InterruptedException.class);
            ensureClassInitialized(Cleaner.class);
        }
        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }	
        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
    }

说明:根据以上源码,我们可以看到ReferenceHandler其实是一个线程,在初始化阶段就加载并链接InterruptedException.class、Cleaner.class两个类;它主要逻辑就是死循环执行 tryHandlePending(true)方法--即一直循环尝试处理penging状态的引用对象;

Reference.tryHandlePending

    static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    r = pending;
                    //'instanceof'有时可能会抛出OutOfMemoryError
                    // 所以,在将'r'从'pending'链中解除链接之前,先这样做……
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    // 解除'r'与'pending'链的链接
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    // 等待锁可能会导致OutOfMemoryError错误
                    // 因为它可能会尝试分配异常对象。
                    if (waitForNotify) {
                        lock.wait();
                    }
                    // 如果等待,重试
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            // 为其他线程提供CPU时间,这样它们就有可能丢弃一些动态引用,并让GC回收一些空间
            // 同时防止CPU密集旋转,以防上面的'r instanceof Cleaner'持续抛出OOME一段时间…
            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状态的引用对象,将其入队RQ队列;需要特别说明的是,如果引用对象类型为Cleaner,则直接调用Cleaner.clean()完成Cleaner构造时闯入的Thread的自定义清理动作;

ReferenceQueue类

public class ReferenceQueue<T> {
    public ReferenceQueue() { }
    //引队列实现类,其实就只一个方法,就是重写入队操作,永远返回false;即不可入队
    private static class Null<S> extends java.lang.ref.ReferenceQueue<S> {
        boolean enqueue(Reference<? extends S> r) {
            return false;
        }
    }
    //表示没有队列
    static java.lang.ref.ReferenceQueue<Object> NULL = new java.lang.ref.ReferenceQueue.Null<>();
    //表示引用已入队
    static java.lang.ref.ReferenceQueue<Object> ENQUEUED = new java.lang.ref.ReferenceQueue.Null<>();

    static private class Lock { };
    private java.lang.ref.ReferenceQueue.Lock lock = new java.lang.ref.ReferenceQueue.Lock();
    private volatile Reference<? extends T> head = null;
    private long queueLength = 0;
    //封装入队操作
    boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
        synchronized (lock) {
            // 检查在获得锁之后,这个引用还没有被加入队列(甚至已经被删除)
            java.lang.ref.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++;
            //如果为FinalReference实例,则将将VM的FinalRefCount加1
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(1);
            }
            lock.notifyAll();
            return true;
        }
    }

    @SuppressWarnings("unchecked")
    private Reference<? extends T> reallyPoll() {       /* Must hold lock */
        Reference<? extends T> r = head;
        if (r != null) {
            head = (r.next == r) ?
                    null :
                    r.next; // Unchecked due to the next field having a raw type in Reference
            r.queue = NULL;
            r.next = r;
            queueLength--;
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(-1);
            }
            return r;
        }
        return null;
    }

    /**
     * 轮询该队列以查看引用对象是否可用。 如果一个是可用的,那么它立即将从队列中删除并返回。 否则,该方法立即返回<tt>null</tt>。 
     *
     * @return  A reference object, if one was immediately available,
     *          otherwise <code>null</code>
     */
    public Reference<? extends T> poll() {
        if (head == null)
            return null;
        synchronized (lock) {
            return reallyPoll();
        }
    }

    /**
     * 删除此队列中的下一个引用对象,阻塞直到其中一个可用或给定的超时时间到期。
     *
     * <p> 这个方法不提供实时保证:它调度超时就像调用{@link Object#wait(long)}方法一样
     *
     * @param  timeout  If positive, block for up to <code>timeout</code>
     *                  milliseconds while waiting for a reference to be
     *                  added to this queue.  If zero, block indefinitely.
     *
     * @return  A reference object, if one was available within the specified
     *          timeout period, otherwise <code>null</code>
     *
     * @throws  IllegalArgumentException
     *          If the value of the timeout argument is negative
     *
     * @throws  InterruptedException
     *          If the timeout wait is interrupted
     */
    public Reference<? extends T> remove(long timeout)
            throws IllegalArgumentException, InterruptedException
    {
        if (timeout < 0) {
            throw new IllegalArgumentException("Negative timeout value");
        }
        synchronized (lock) {
            Reference<? extends T> r = reallyPoll();
            if (r != null) return r;
            long start = (timeout == 0) ? 0 : System.nanoTime();
            for (;;) {
                lock.wait(timeout);
                r = reallyPoll();
                if (r != null) return r;
                if (timeout != 0) {
                    long end = System.nanoTime();
                    timeout -= (end - start) / 1000_000;
                    if (timeout <= 0) return null;
                    start = end;
                }
            }
        }
    }

    /**
     * 移除队列中的下一个引用对象,阻塞直到有一个可用。
     *
     * @return A reference object, blocking until one becomes available
     * @throws  InterruptedException  If the wait is interrupted
     */
    public Reference<? extends T> remove() throws InterruptedException {
        return remove(0);
    }

}

总结:

  1. Reference 框架的设计与JVM垃圾收集时息息相关的,主要用于更加灵活的控制对象的生死,其实现类似于事件处理,可以是 JVM 默认处理,也可以是用户自定义的处理逻辑;

  2. 在 Java 语言中 Reference 类定义了子类(SoftReference,WeakReference,PhantomReference)的主要逻辑,但是判断引用回收的条件主要在 JVM 中定义(主要发生在 GC 标记阶段),如果你有兴趣可以到 OpenJDK 里面继续深入研究;

  3. 如果在使用 Reference 的时候传入了 ReferenceQueue,即使用自定义的逻辑处理,那么最后一定要把 ReferenceQueue 中注册的 Reference 移除,因为此时 GC 不会回收 ReferenceQueue 中的链表;

  4. Reference Handler 是在Reference类加载过程的初始化阶段调用类构造器<clinit>方法时创建,由于Reference类的加载只会发生一次,所以该线程只有一个,但是 Reference 链表却有很多条(所以在注册的时候需要加锁),另外每个 Class 对象都能同时生成多个引用对象,并注册 ReferenceQueue ;

Reference整体核心源码分析如下:

public abstract class Reference<T> {

    /*
     * 状态在队列和next字段中编码如下:
     *
     *     Active: 注册队列的实例 queue = ReferenceQueue.  如果没有注册队列的实例,则为queue=ReferenceQueue.NULL; next=null
     *
     *     Pending: 注册队列的实例 queue = ReferenceQueue ;  next = this
     *
     *     Enqueued: queue = ReferenceQueue.ENQUEUED; next =在队列中的下一个实例,或者this(如果在列表末尾)。
     *
     *     Inactive: queue = ReferenceQueue.NULL; next = this.
     *
     *在这种模式下,收集器只需要检查下一个字段,以确定一个Reference实例是否需要特殊处理:如果下一个字段为空,那么实例是活动的; 
     *如果它是非空的,那么收集器应该正常处理实例。
     */
    // reference包装的原对象
    private T referent;         /* GC专门处理 */
    //引用队列(用于创建对象时,注册了RQ的对象的排队,一般需要用户自定义线程从队列里取出R对象处理)
    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:   GC维护的discovered引用列表中的下一个元素 (如果是最后一个,则为this)
     *     pending:   待处理列表中的下一个元素 (如果是最后一个,则为null )
     *   otherwise:   NULL
     */
    transient private Reference<T> discovered;  /* used by VM */

    /*
     *对象,用于与垃圾收集器同步。 收集器必须在每次收集周期开始时获取此锁。 因此,至关重要的是,任何持有此锁的代码都要尽快完成,不分配新对象,        *并避免调用用户代码
     */
    static private class Lock { }
    private static Lock lock = new Lock();

    /*
     *等待加入队列的引用列表。 收集器将引用添加到此列表,而引用处理程序线程将删除它们。 该列表受上述锁对象的保护。 
     *列表使用发现的字段链接其元素。
     */
    private static Reference<Object> pending = null;

    /*
     *高优先级线程去入队待处理的引用
     */
    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 {
            // 预加载并初始化InterruptedException和Cleaner类,这样我们在惰性加载/初始化它们时,如果内存不足,就不会在运行循环中遇到麻烦。
            ensureClassInitialized(InterruptedException.class);
            ensureClassInitialized(Cleaner.class);
        }

        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }

        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
    }

    /**
     * 如果有待处理的{@link Reference},尝试处理。
     *返回{@code true}以提示可能还有一个待处理的{@link Reference},或者当此刻没有待处理的{@link Reference},
     *程序可以做一些其他有用的工作而不是循环时,返回{@code false}。
     *
     * @param waitForNotify如果{@code true}并且没有待处理的{@link Reference},等待直到VM通知或中断;
     * 如果{@code false},当没有待处理的{@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'有时可能会抛出OutOfMemoryError
                    // 所以,在将'r'从'pending'链中解除链接之前,先这样做……
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    // 解除'r'与'pending'链的链接
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    // 等待锁可能会导致OutOfMemoryError错误
                    // 因为它可能会尝试分配异常对象。
                    if (waitForNotify) {
                        lock.wait();
                    }
                    // 如果等待,重试
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            // 为其他线程提供CPU时间,这样它们就有可能丢弃一些动态引用,并让GC回收一些空间
            // 同时防止CPU密集旋转,以防上面的'r instanceof Cleaner'持续抛出OOME一段时间…
            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");
        /*
         *设置最大优先级
         */
        handler.setPriority(Thread.MAX_PRIORITY);
        //设置为守护线程
        handler.setDaemon(true);
        handler.start();

        // 在 SharedSecrets中提供访问
        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            @Override
            public boolean tryHandlePendingReference() {
                return tryHandlePending(false);
            }
        });
    }

    /* -- 引用访问器和设置器 -- */

    /**
     * 返回此引用对象的引用.  如果该引用对象已被程序或垃圾收集器清除,则此方法返回<code>null</code>。
     */
    public T get() {
        return this.referent;
    }

    /**
     * 清除此引用对象。 调用此方法不会导致此对象进入队列。
     *
     * 此方法仅由Java代码调用; 当垃圾收集器清除引用时,它直接这样做,而不调用此方法。
     */
    public void clear() {
        this.referent = null;
    }


    /* -- Queue operations -- */

    /**
     *告诉该引用对象是否已被程序或垃圾收集器加入队列。 如果这个引用对象在创建时没有注册到队列中,那么这个方法将始终返回<code>false</code>。 
     *
     * 当且仅当此引用对象已加入队列 @return   <code>true</code>
     */
    public boolean isEnqueued() {
        return (this.queue == ReferenceQueue.ENQUEUED);
    }

    /**
     * 如果有的话,将这个引用对象添加到注册它的队列中。
     *
     * <p>该方法仅由Java代码调用; 当垃圾收集器队列引用时,它直接这样做,而不调用此方法。
     *
     *@return 如果该引用成功加入队列, <code>true</code>; 如果它在创建时已经加入队列或者没有注册到队列中 ,<code>false</code>
     */
    public boolean enqueue() {
        return this.queue.enqueue(this);
    }


    /* -- Constructors -- */

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

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

}
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部