转载自:http://www.jianshu.com/p/f86d3a43eec5
              http://www.cnblogs.com/jabnih/p/6580665.html

ReferenceQueue

引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中,ReferenceQueue实现了入队(enqueue)和出队(poll),还有remove操作,内部元素head就是泛型的Reference:

    private volatile Reference<? extends T> head = null;

ReferenceQueue的实现是由Reference自身的链表结构(单向循环链表)所实现的。所以说ReferenceQueue名义上是一个队列,但实际内部并非有实际的存储结构,它的存储是依赖于内部节点之间的关系来表达。来看下它的入队方法:

    boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */synchronized (lock) {// Check that since getting the lock this reference hasn't already been// enqueued (and even then removed)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++;if (r instanceof FinalReference) {sun.misc.VM.addFinalRefCount(1);}lock.notifyAll();return true;}}

从enqueue()代码可以看出来,queue仅存储当前的head节点,而后面的节点由每个reference节点通过自己的next来保持的。而且queue是一个后进先出的队列。当新的节点进入时成为了head,然后出队时也是先出的head。

Reference

java.lang.ref.Reference是SoftReference、WeakReference、PhantomReference的基类。Reference对象是和垃圾回收密切配合实现,Reference的直接子类都是由JVM定制化处理的,因此在代码中直接继承于Reference类型没有任何作用。但可以继承JVM定制的Reference的子类。例如:Cleaner就继承了PhantomReference。

构造函数

Reference内部提供2个构造函数,一个带queue,一个不带queue。

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

其中queue的意义在于可以在外部对这个queue进行监控。如果有对象即将被回收,那么相应的reference对象就会被放到这个queue里,我们就拿到reference再作一些事情。而如果不带的话,就只有不断地轮询reference对象,通过判断里面的get是否返回null,来判断是否被回收。phantomReference对象不能这样作,其get始终返回null,因此它只有带queue的构造函数。queue的默认值是ReferenceQueue.NULL。
       以上这两种构造方法均有相应的使用场景,取决于实际的应用。如WeakHashMap中就选择去查询queue的数据,来判定是否有对象将被回收。而ThreadLocalMap则采用判断get()是否为null来作处理。
       如果在创建一个引用对象时,指定了ReferenceQueue,那么当引用对象指向的对象达到合适的状态(根据引用类型不同而不同)时,GC会把引用对象本身添加到这个队列中,方便我们处理它,因为“引用对象指向的对象GC会自动清理,但是引用对象本身也是对象(是对象就占用一定资源),所以需要我们自己清理。”

Reference内部主要的成员

    private T referent;         /* Treated specially by GC */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:   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*/transient private Reference<T> discovered;  /* used by VM *//* 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.*/private static Reference<Object> pending = null;

referent表示其引用的对象,即在构造的时候需要被包装在其中的对象。
         queue是对象即将被回收时所要通知的队列。当对象即将被回收时,整个reference对象,而不仅仅是被回收的对象,会被放到queue里面,然后外部程序即可通过监控这个queue拿到相应的数据了。
         next即当前引用节点所存储的下一个即将被处理的节点。但next仅在放到queue中才会有意义,因为只有在enqueue的时候,会将next设置为下一个要处理的Reference对象。为了描述相应的状态值,在放到队列当中后,其queue就不会再引用这个队列了。而是引用一个特殊的ENQUEUED(内部定义的一个空队列)。因为已经放到队列当中,并且不会再次放到队列当中。
        discovered表示要处理的对象的下一个对象。即可以理解要处理的对象也是一个链表,通过discovered进行排队,这边只需要不停地拿到pending,然后再通过discovered不断地拿到下一个对象赋值给pending即可,直到取到了最有一个。它是被JVM使用的。
        pending是等待被入队的引用列表。JVM收集器会添加引用到这个列表,直到Reference-handler线程移除了它们。这个列表使用discovered字段来连接它下一个元素(即pending的下一个元素就是discovered对象。r = pending; pending = r.discovered)。

Reference状态值

每个引用对象都有相应的状态描述,即描述自己以及其包装的对象当前处于一个什么样的状态,以方便进行查询,定位或处理。Reference有4种状态。四种状态用Reference的成员变量queue与next来标示。
       Active:活动状态,新创建的引用对象都是这个状态,即相应的对象为强引用状态。在这个状态下next == null,queue为构造Reference对象时传入的ReferenceQueue对象,默认是ReferenceQueue.NULL。在GC检测到引用对象的可达性发生变化之后,它的状态将变化为Pending或Inactive(如果引用对象在构造时指定了ReferenceQueue,那么转移到Pending;如果没指定,转移到Inactive)。
       Pending:准备放入queue当中的引用对象,在这个状态的对象将挨个地排队放到queue当中。在这个时间窗口期,相应的对象为Pending状态,都在pending列表中。此状态的下,next == this(由JVM设置),queue为定义时所引用的queue。这里需要注意的是,queue如果指定了,那么引用对象能转移到Pending,如果没指定,直接转移到Inactive,因为此时next == this,queue == ReferenceQueue.NULL。
       Enqueued:引用对象已经放到queue当中了。准备由外部线程来询循queue获取相应的数据。调用ReferenceQueue.enqueued()后的reference就会处于这个状态中。此状态中next 为该queue中的下一个引用,如果是该队列中的最后一个,那么为this,queue = ReferenceQueue.ENQUEUED。当reference实例从它的ReferenceQueue移除后,它将成为Inactive。没有注册queue的实例不会进入这个状态。
       Inactive:即此对象已经由外部从queue中获取到,并且已经处理掉了,此时next == this,queue == ReferenceQueue.NULL。即意味着此引用对象可以被回收,并且对内部封装的对象也可以被回收掉了。但实际的回收运行取决于clear()方法是否被调用(clear()方法会设置this.referent = null)。

Reference类加载后,就会启动Reference Handler线程

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

从源码可以看出,这个线程在Reference类的static构造块中启动,并且被设置为最高优先级MAX_PRIORITY和daemon状态,并随即启动该线程。

    private static class ReferenceHandler extends Thread {ReferenceHandler(ThreadGroup g, String name) {super(g, name);}public void run() {for (;;) {Reference<Object> r;synchronized (lock) {if (pending != null) {r = pending;pending = r.discovered;r.discovered = null;} else {try {try {lock.wait();} catch (OutOfMemoryError x) { }} catch (InterruptedException x) { }continue;}}// Fast path for cleanersif (r instanceof Cleaner) {((Cleaner)r).clean();continue;}ReferenceQueue<Object> q = r.queue;if (q != ReferenceQueue.NULL) q.enqueue(r);}}}

此线程要做的事情就是不断的检查pending是否为null,如果pending不为null,则将pending进行enqueue,否则线程进入wait状态。
       当Reference内部的referent对象的可达状态改变时,JVM会将该Reference对象放入pending链表,注意这时JVM会设置referent对象的next = this。并且这里enqueue的队列是在初始化Reference对象时传进来的queue,如果传入了null(实际使用的是ReferenceQueue.NULL),则ReferenceHandler则不进行enqueue操作,这时referent对象的状态已经是Inactive了。只有非RefernceQueue.NULL的queue才会将Reference进行enqueue。
           在enqueue()中,当reference实例的queue!=null && queue != ENQUEUED时;设置queue为ENQUEUED,next为下一个要处理的reference对象,或者若为最后一个则next==this。这时referent对象是Enqueue。
            当调用queue的remove或者poll方法时,就会将要处理的reference实例的queue设置为ReferenceQueue.NULL,next = this,此时reference实例就进入了Inactive状态,等待JVM回收。

具体执行例子:

// 创建一个引用队列
ReferenceQueue queue = new ReferenceQueue();// 创建虚引用,此时状态为Active,并且Reference.pending为空,当前Reference.queue = 上面创建的queue,并且next=null
WeakReference reference = new WeakReference(new Object(), queue);
System.out.println(reference);
// 当GC执行后,由于是虚引用,所以回收该object对象,并且置于pending上,此时reference的状态为PENDING
System.gc();/* ReferenceHandler从pending中取下该元素,并且将该元素放入到queue中,此时Reference状态为ENQUEUED,Reference.queue = ReferenceENQUEUED *//* 当从queue里面取出该元素,则变为INACTIVE,Reference.queue = Reference.NULL */
Reference reference1 = queue.remove();
System.out.println(reference1);

ReferenceQueue、Reference详解相关推荐

  1. Reference详解

    1.   概述 图片引用自https://blog.csdn.net/u011801189/article/details/52723868 根据Reference这个类里面的注释,整个Referen ...

  2. java reference详解_你不可不知的Java引用类型之——Reference源码解析

    定义 Reference是所有引用类型的父类,定义了引用的公共行为和操作. reference指代引用对象本身,referent指代reference引用的对象,下文介绍会以reference,ref ...

  3. java reference详解_Java Reference详解

    Reference是java中的引用类,它用来给普通对像进行包装,从而在JVM在GC时,按照引用类型的不同,在回收时采用不同的逻辑.先来看下这个类的继承体系: image.png 在进行对这些子类进行 ...

  4. 详解JVM内存管理与垃圾回收机制5 - Java中的4种引用类型

    在Java语言中,除了基础数据类型的变量以外,其他的都是引用类型,指向各种不同的对象.在前文我们也已经知道,Java中的引用可以是认为对指针的封装,这个指针中存储的值代表的是另外一块内存的起始地址(对 ...

  5. Java开发常见面试题详解(并发,JVM)

    预览 并发 问题 详解 请谈谈你对volatile的理解 link CAS你知道吗? link 原子类Atomiclnteger的ABA问题谈谈?原子更新引用知道吗? link 我们知道ArrayLi ...

  6. JVM垃圾回收算法与原理详解

    垃圾回收 参考文档 GC参考手册-Java版 理解Java的强引用.软引用.弱引用和虚引用 JVM系列(五) - JVM垃圾回收算法 如何判断对象可以回收 引用计数法 参考文章 Java JVM的引用 ...

  7. Java开发常见面试题详解(JVM)_2

    Java开发常见面试题详解(JVM)_2 JVM 问题 详解 JVM垃圾回收的时候如何确定垃圾?是否知道什么是GC Roots link 你说你做过JVM调优和参数配置,请问如何盘点查看JVM系统默认 ...

  8. Unsafe应用详解

    本文章来源于:https://github.com/Zeb-D/my-review ,请star 强力支持,你的支持,就是我的动力. [TOC] 背景 平时在啃并发库会发现一些底层的影子,虽然J.U. ...

  9. java jstack使用_jstack命令详解

    jstack命令可以用来查看运行的Java进程下,多线程的运行情况,语句如"[root@admin ~]# jstack 43616 Full thread dump Java HotSpo ...

最新文章

  1. scater分析单细胞转录组数据代码
  2. Permutation Sequence
  3. 013 Android锁机病毒分析
  4. getSystemService() in Android
  5. Kindle的对手来了?华为首款鸿蒙墨水平板国行发布时间曝光...
  6. Win11如何给系统盘瘦身?Win11系统盘瘦身方法
  7. Material101-Lec13-18 Rian Shader
  8. 星巴克推出Web3平台;天啦噜,AI绘画能007了;『决策算法』电子书;合成人脸数据集;面向数据的版本控制;前沿论文 | ShowMeAI资讯日报
  9. 全渠道营销与多渠道营销:定义、比较、示例
  10. Extjs--关于文件的下载
  11. 女子花2万元雇友人杀逝世女街坊
  12. 台达变频器vfb—d参数表_台达DPS
  13. OpenXml操作Word的一些操作总结. - 天天不在
  14. PCIe扫盲——弹性缓存(Elastic Buffer/ CTC Buffer)
  15. c#和python_Python3 与 C# 基础语法对比(就当Python和C#基础的普及吧)
  16. android中自定义 toast,android 自定义Toast
  17. uin-app 小程序,获取手机号
  18. 巧学JAVA--------扑克牌小游戏
  19. 解读TestBird手游兼容性测试白皮书
  20. Android 手写字体识别

热门文章

  1. VL53L0测距芯片试用【ST主题月】
  2. 深度linux触摸板失灵,解决笔记本安装deepin OS触摸板、WIFI无法使用问题
  3. 采用ODC改善软件质量:一个案例研究
  4. 插入u盘有图标但计算机没盘,没插U盘却有U盘图标显示的原因和解决方法
  5. vue项目导出word文件(根据word模板导出)
  6. 访问远程数据库时,产生Access denied for user ‘root‘@‘xxx.xxx.xxx.xxx‘ (using password: YES)异常的解决办法
  7. 计算机教程打字方法,电脑快速打字方法教程
  8. Android 获取最近几天的日历日程
  9. 笔记2 IKAnalyzer扩展词库
  10. python怎样分析文献综述_教你如何做文献综述