`

WeakReference &&reference quene &&GC

阅读更多

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

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 .gc Weak Reference 马上会被回收 , 怎么做到的呢 ? 让我们一起来深入 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正是利用此来实现的。

 

用图来大致表示如下:


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics