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

用图来大致表示如下:

 
 

WeakReference &&reference quene &&GC的更多相关文章

  1. WeakReference Reference ReferenceQueue

    public class WeakReference<T> extends Reference<T> { public WeakReference(T referent) { ...

  2. Reference Counting GC (Part one)

    目录 引用计数法 计数器值的增减 new_obj()和update_ptr()函数 new_obj()生成对象 update_ptr()更新指针ptr,对计数器进行增减 优点 可即可回收垃圾 最大暂停 ...

  3. Reference Counting GC (Part two :Partial Mark & Sweep)

    目录 部分标记清除算法 前提 dec_ref_cnt()函数 new_obj()函数 scan_hatch_queue()函数 paint_gray()函数 scan_gray()函数 collect ...

  4. Java Reference 源码分析

    @(Java)[Reference] Java Reference 源码分析 Reference对象封装了其它对象的引用,可以和普通的对象一样操作,在一定的限制条件下,支持和垃圾收集器的交互.即可以使 ...

  5. 深入理解StrongReference,SoftReference, WeakReference和PhantomReference

    Java 中一共有 4 种类型的引用 : StrongReference. SoftReference. WeakReference 以及 PhantomReference (传说中的幽灵引用 呵呵) ...

  6. GC真正的垃圾:强、软、弱、和虚 对象

    垃圾回收的基本思想就是判断一个对象是否可触及性,说白了就是判断一个对象是否可以访问,如果对象对引用了,说明对象正在被使用,如果发现对象没有被引用,说明对象已经不再使用了,不再使用的对象可以被回收,但是 ...

  7. JDK源码分析(8)之 Reference 完全解读

    在阅读本文之前最好对 Reference 框架有一个整体的把握,可以参考我上一篇博客 Reference 框架概览 :本文主要讲了 Reference 的子类实现和应用(SoftReference,W ...

  8. java Reference

    相关讲解,参考: Java Reference 源码分析 Java Reference详解 Reference: // 名称说明下:Reference指代引用对象本身,Referent指代被引用对象 ...

  9. SoftReference、WeakReference、PhantomRefrence分析和比较

    级别 什么时候被垃圾回收 用途 生存时间 强引用 从来不会 对象的一般状态 JVM停止运行时终止 软引用 在内存不足时 优化内存使用 内存不足时终止 弱引用 在垃圾回收时 对象缓存 gc运行后终止 虚 ...

随机推荐

  1. 计算两个日期之间的天数差C++/java

    1--Java 分析:调用java中Calendar类 int days(Date date1,Date date2){ Calendar cal = new Calendar.getInstance ...

  2. 【转】log4js在PM2的cluster模式下大坑

    请直接查看原文:https://blog.yourtion.com/fix-log4js-with-pm2-not-work.html 之前一直使用 debug 还有 console.log 去打日志 ...

  3. 【bzoj1822】[JSOI2010]Frozen Nova 冷冻波 计算几何+二分+网络流最大流

    题目描述 WJJ喜欢“魔兽争霸”这个游戏.在游戏中,巫妖是一种强大的英雄,它的技能Frozen Nova每次可以杀死一个小精灵.我们认为,巫妖和小精灵都可以看成是平面上的点. 当巫妖和小精灵之间的直线 ...

  4. 【bzoj4259/bzoj4503】残缺的字符串/两个串 FFT

    bzoj4259 题目描述 很久很久以前,在你刚刚学习字符串匹配的时候,有两个仅包含小写字母的字符串A和B,其中A串长度为m,B串长度为n.可当你现在再次碰到这两个串时,这两个串已经老化了,每个串都有 ...

  5. element el-cascader设置默认值

    原文:https://www.jianshu.com/p/b690d7fe6ec0 注意两点就行了 <el-form-item label="AP名称"> <el ...

  6. pat 1074. 宇宙无敌加法器(20)

    1074. 宇宙无敌加法器(20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 地球人习惯使用十进制数,并且默 ...

  7. cmp 指令

    (lldb) disassemble -n comp2 untitled6`comp2: 0x10d065f40 <+>: pushq %rbp 0x10d065f41 <+> ...

  8. POJ3104 Drying

    Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 13703   Accepted: 3527 Description It i ...

  9. 比较全的.NET页面缓存技术文章

    原文发布时间为:2009-11-04 -- 来源于本人的百度文章 [由搬家工具导入] http://www.cnblogs.com/jacksonwj/archive/2009/07/09/15197 ...

  10. JavaScript内部是这样运行

    编译阶段 词法分析(Lexing) 这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代 码块被称为词法单元(token). 简单举个例子:c = b - a 转换为 NAME ...