zoukankan      html  css  js  c++  java
  • WeakReference &&reference quene &&GC

    在了解WeakReference之前,先给出一段简单的代码:

    public class WeakReferenceTest {
    public static void main(String[] args) throws Exception {
    Object o = new Object();
    // 默认的构造函数,会使用ReferenceQueue.NULL 作为queue
    WeakReference<Object> wr = new WeakReference<Object>(o);
    System.out.println(wr.get() == null);
    o = null;
    System.gc();
    System.out.println(wr.get() == null);
    }
    }
    输出结果:false,true

    喜欢探求究竟的童鞋会问,为啥System.gcWeakReference马上会被回收,怎么做到的呢?让我们一起来深入Reference的源码探求个究竟.内部有两点需要注意:

    1)pending和 discovered成员:

    先看:pending对象

    /* 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.
    */
    private static Reference pending = null;
    这个对象,定义为private,并且全局没有任何给它赋值的地方,根据它上面的注释,我们了解到这个变量是和垃圾回收期打交道的

    再看discovered,同样为private,上下文也没有任何地方使用它

    transient private Reference<T> discovered;    /* used by VM */

    看到了它的注释也明确写着是给VM用的。

        上面两个变量对应在VM中的调用,可以参考openjdk中的hotspot源码,在hotspot/src/share/vm/memory/referenceProcessor.cpp 的ReferenceProcessor::discover_reference 方法。(根据此方法的注释由了解到虚拟机在对Reference的处理有ReferenceBasedDiscovery和RefeferentBasedDiscovery两种策略)

    2)ReferenceHandler 线程

    这个线程在Reference类的static构造块中启动,并且被设置为高优先级和daemon状态。

    此线程要做的事情,是不断的检查pending 是否为null,如果pending不为null,则将pending进行enqueue,否则线程进入wait状态。

    通过这2点,我们来看整个过程:

    pending是由jvm来赋值的,当Reference内部的referent对象的可达状态改变时,jvm会将Reference对象放入pending链表。

    结合代码eg1中的 o = null; 这一句,它使得o对象满足垃圾回收的条件,并且在后边显式的调用了 System.gc(),垃圾收集进行的时候会标记WeakReference所referent的对象o为不可达(使得wr.get()==null),并且通过 赋值给pending ,触发ReferenceHandler线程处理pending。

    ReferenceHandler线程要做的是将pending对象enqueue,但默认我们所提供的queue,也就是从构造函数传入的是null,实际是使用了ReferenceQueue.NULL,Handler线程判断queue为ReferenceQueue.NULL则不进行操作,只有非ReferenceQueue.NULL 的queue才会将Reference进行enqueue。

    ReferenceQueue.NULL相当于我们提供了一个空的Queue去监听垃圾回收器给我们的反馈(什么反馈呢?是说这个quene是给我们来用的么,例如WeakHashMap中使用的那种方式),并且对这种反馈不做任何处理。(但垃圾还是回收了???不是poll时候做的事情么?)

    要处理反馈,则必须要提供一个非ReferenceQueue.NULL的queue。这个quene可以看做是GC与应用程序的一个桥梁,告知应用需要对那些reference进行处理.

    当一个 WeakReference 开始返回 null 时, 它所指向的对象已经准备被回收, 这时可以做一些合适的清理工作.   将一个 ReferenceQueue 传给一个 Reference 的构造函数, 当对象被回收时, 虚拟机会自动将这个对象插入到 ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 来清除 key 已经没有强引用的 entries.

    在WeakHashMap则在内部提供了一个非NULL的ReferenceQueue

        private final ReferenceQueue<K> queue = new ReferenceQueue<K>();

    在 WeakHashMap 添加一个元素时,会使用 此queue来做监听器。

    见put方法中的下面一句:

    tab[i] = new Entry<K,V>(k, value, queue, h, e);
    这里Entry是一个内部类,继承了WeakReference
    class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V>
    WeakHashMap的 put, size, clear 都会间接或直接的调用到 expungeStaleEntries()方法。

    顾名思义,此方法的作用就是将 queue中陈旧的Reference进行删除,因为其内部的referent都已经不可达了。所以也将这个WeakReference包装的key从map中删除。

    总结:ReferenceQueue是作为 JVM GC与上层Reference对象管理之间的一个消息传递方式,它使得我们可以对所监听的对象引用可达发生变化时做一些处理,WeakHashMap正是利用此来实现的。

    用图来大致表示如下:

     
     
  • 相关阅读:
    java核心学习(十六) javaIO框架---Process类的流,读写其他进程
    java核心学习(十五) IO框架---重定向标准输入输出
    java核心学习(十四) IO框架---推回输入流
    java核心学习(十三) IO框架---转换流和缓冲流
    java核心学习(十二) IO框架---理解IO流
    递推+矩阵快速幂 HDU 2065
    树形DP hdu1520
    二分图之最小路径覆盖 HDU1151
    二分图之最小独立集 HDU 2768
    最短路 POJ2267
  • 原文地址:https://www.cnblogs.com/hushaojun/p/4948438.html
Copyright © 2011-2022 走看看