做安卓开发的人都知道,其用的是Java语言开发的,所以标题自然也是成立的。
以前设计缓存时也曾过用WeakHashMap来实现,对Java的Reference稍做过一些了解,其实这个问题,归根到底,是个Java GC的问题,由垃圾回收器与ReferenceQueue的交互方式决定的。WeakHashMap的实现也是通过ReferenceQueue这个“监听器”来优雅的实现自动删除那些引用不可达的key的。
先看看ReferenceQueue在Java中的描述:
Reference queues, to which registered reference objects are appended by the garbage collector after the appropriate reachability changes are detected. 中文JavaDoc的描述:引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中
查看源代码会发现它很简单,实现了一个队列的入队(enqueue)和出队(poll还有remove)操作,内部元素就是泛型的Reference,并且Queue的实现,是由Reference自身的链表结构所实现的。
再来看 Reference类的代码,注意,javadoc中有一句,提到了它与GC是紧密相关的:
Because reference objects are implemented in close cooperation with the garbage collector, this class may not be subclassed directly.
从数据结构上看,Reference链表结构内部主要的成员有
private T referent; //就是它所指引的 Reference next; //指向下一个;
另一个比较重要的内部数据是:
ReferenceQueue queue;
这个queue是通过构造函数传入的,表示创建一个Reference时,要将其注册到那个queue上。
Queue的另一个作用是可以区分不同状态的Reference。Reference有4种状态,不同状态的reference其queue也不同:
- Active: queue = ReferenceQueue with which instance is registered, or ReferenceQueue.NULL if it was not registered with a queue; next = null. [/cpp]
- Pending: queue = ReferenceQueue with which instance is registered; next = Following instance in queue, or this if at end of list. [/cpp]
- Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instancein queue, or this if at end of list. [/cpp]
- Inactive: queue = ReferenceQueue.NULL; next = this. [/cpp]
那么,当我们创建了一个WeakReference,并且将其referent改变后,究竟发生了什么?先看一段代码:
// eg1 public static void test() throws Exception{ Object o = new Object(); // 默认的构造函数,会使用ReferenceQueue.NULL 作为queue WeakReferencewr = new WeakReference(o); System.out.println(wr.get() == null); o = null; System.gc(); System.out.println(wr.get() == null); }
结果大家都知道,但其内部是怎么实现的,还需重新看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,并且全局没有任何给它赋值的地方, //根据它上面的注释,我们了解到这个变量是和垃圾回收期打交道的。 [/cpp] 再看discovered,同样为private,上下文也没有任何地方使用它 transient private Reference
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去监听垃圾回收器给我们的反馈,并且对这种反馈不做任何处理。要处理反馈,则必须要提供一个非ReferenceQueue.NULL的queue。
在WeakHashMap则在内部提供了一个非NULL的ReferenceQueue private final ReferenceQueue
expungeStaleEntries顾名思义,此方法的作用就是将 queue中陈旧的Reference进行删除,因为其内部的referent都已经不可达了。所以也将这个WeakReference包装的key从map中删除。
个人认为:ReferenceQueue是作为 JVM GC与上层Reference对象管理之间的一个消息传递方式,它使得我们可以对所监听的对象引用可达发生变化时做一些处理,WeakHashMap正是利用此来实现的。用图来大致表示如下:
- 文章2305
- 用户1336
- 访客11455432
没有努力,天份不代表什么。
MySQL 数据库优化
This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de
免ROOT实现模拟点击任意位置
Mobaxterm终端神器
CreateProcessW要注意的细节问题
Autonomous NAT Traversal
【教程】win10 彻底卸载edge浏览器
eclipse工程基于Xposed的一个简单Hook
排名前5的开源在线机器学习
Mac OS最简单及(Karabiner)快捷键设置
发一款C++编写的麻将
VMware NAT端口映射外网访问虚拟机linux
独家发布最新可用My-AutoPost——wordpress 采集器