Reference 和 Reference queue 精髓源码分析

目录

  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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • ReferenceQueue 引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中 ...
    tomas家的小拨浪鼓阅读 37,027评论 10 59
  • 引用的分类 Java 1.2以后,除了普通的引用外,Java还定义了软引用、弱引用、虚引用等概念。 强引用:GC ...
    刘惜有阅读 962评论 0 1
  • 感知GC。怎么感知:* 通过get来判断已经被GC(PhantomReference 在任何时候get都是null...
    YDDMAX_Y阅读 1,996评论 0 4
  • 当我们还是学生的时候,每天的生活简单又压抑。对这样的感觉谈不上满意或者不满,因为我们并不知道外面是什么样的,光凭想...
    肥木有阅读 559评论 0 2
  • 我叫小瑶,我和我的丈夫生活幸福。一直认为这样的幸福会持续一辈子。可是,就在我怀孕四个月的时候,他突然失踪了。我终日...
    胡莘瑶阅读 332评论 0 0

友情链接更多精彩内容