美文网首页
Reference 和 Reference queue 精髓源码

Reference 和 Reference queue 精髓源码

作者: 良辰夜 | 来源:发表于2018-07-13 17:07 被阅读0次

    目录

    1. Reference 状态
      1.1 Active
      1.2 Pending
      1.3 Enqueued
      1.4 Inactive
    2. reference-hannel 线程
      2.1 启动线程
      2.2 线程执行

    1.Reference 状态

    每个Reference状态列表是Active、Pending、Enqueued、Inactive (本文也将基于以下这四种状态做出源码解读)

    状态间主要的标识:
    用Reference的成员变量queue与next(类似于单链表中的next)来标识

    ReferenceQueue<? super T> queue;
    Reference next; //当状态不为active,gc自动赋值为this
    

    有了这些约束,GC 只需要检测next字段就可以知道是否需要对该引用对象(reference)采取特殊处理

    • 如果next为null,那么说明该引用为Active状态
    • 如果next不为null,那么 GC 应该按其正常逻辑处理该引用。

    什么特殊处理?
    GC在状态active发送变化时,会主动把 weakReference中的 reference置null,PhantomReference 中的 reference 无视 (PhantomReference的get方法重写返回null,可能就因为这原因就没有置null了),并给next赋值this,以及pending等。(别问我为啥,测出来的。。。。培训出来就这水平了)

    1.1 Active

    描述:
    active 状态下的reference会受到GC的特别对待,GC在发现对象的可达性变成合适的状态后将变更reference状态,具体是变更为pending还是Inactive,取决于是否注册了 queue

    特点:

    //如果构造参数中没指定queue,那么queue为ReferenceQueue.NULL
    //否则为构造参数中传递过来的queue
    queue = ReferenceQueue || ReferenceQueue.NULL
    next = null
    

    源码分析:

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

    1.2 Pending

    描述:
    pending-Reference列表中的引用都是这个状态。
    它们等着被内部线程ReferenceHandler处理(调用ReferenceQueue.enqueue方法)
    没有注册的实例不会进入这个状态!

    特点:

    //ReferenceQueue 是注册的队列,从构造参数哪里传过来的
    queue=ReferenceQueue
    next = this
    

    源码分析:

    1. 想进Pending就必须要注册队列,ReferenceQueue.NULL 是不会enqueue
     static boolean tryHandlePending(boolean waitForNotify) {
           //some code
            ReferenceQueue<? super Object> q = r.queue;
            if (q != ReferenceQueue.NULL) q.enqueue(r);
            return true;
        }
    
    1. next=this,这个应该是GC赋的值,只要reference的state一改变,这个next就莫名其妙被赋值了。

    3.似乎明白了 ReferenceQueue.ENQUEUED 的含义

    public boolean isEnqueued() {
            return (this.queue == ReferenceQueue.ENQUEUED);
        }
    

    1.3 Enqueued

    描述:
    调用ReferenceQueue.enqueued方法后的引用处于这个状态中。
    没有注册的实例不会进入这个状态。

    特点:

    queue = ReferenceQueue.ENQUEUED;
    next = queue的下一个引用, 如果已经queue是最后一个元素,那么就是自身.
    

    源码分析:
    可以看出 整体是一种先进后出的栈结构。head作为栈头。r.next指向成员变量 head,如果head为null,那么就指向自己(为了后面的压栈好找元素)

    private volatile Reference<? extends T> head = null;
    boolean enqueue(Reference<? extends T> r) { 
    //some code
                r.queue = ENQUEUED;
                r.next = (head == null) ? r : head;
                head = r;
    //some code            
    }
    
    

    1.4 Inactive

    描述:
    最终状态,处于这个状态的引用对象,状态不会在改变。

    两种途径到这个状态:
    1.referenceQueue 调用 reallyPoll ,
    2.不注册 referenceQueue,reference state变化时。

    特点:

    queue = ReferenceQueue.NULL;
    next = this
    

    源码分析:

    private Reference<? extends T> reallyPoll() {       /* Must hold lock */
            Reference<? extends T> r = head;
            //some code
                r.queue = NULL;
                r.next = r;
             //some code
        }
    

    2.reference-hannel 线程

    2.1启动线程

    1. 说明jvm一启动线程就跟着启动了
    2. 提供了一个 JavaLangRefAccess 来提供外接接口访问
    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);
                }
            });
        }
    

    2.2 线程执行

    1. pending == null 时,会阻塞线程,所以不会一直调用。gc肯定notify了
    2. Cleaner 是个重要的接口,非常适合做资源回收。
    3. 如果注册了引用队列,会将引用enqueue进去。
    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;
        }
    

    引用:
    https://liujiacai.net/blog/2015/09/27/java-weakhashmap/#%E5%BC%B1%E5%BC%95%E7%94%A8%EF%BC%88weak-reference%EF%BC%89

    相关文章

      网友评论

          本文标题:Reference 和 Reference queue 精髓源码

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