记得在很久之前有写过一篇《Java中的Reference解析》,主要讲的是Java中的四种引用方式与引用队列,不过这些都是基础的理论知识,最近开发项目中有使用到WeakHashMap,对于Java的引用以及引用队列有了更深的了解,在此做个相关总结。

一、WeakHashMap的实现方式

总体来说,WeakHashMap的底层数据结构与HashMap的实现差不多,都是用“拉链法”来实现,主要区别在于WeakHashMap的Entry 继承于WeakReference,并维护一个ReferenceQueue,使其具有了“弱引用的特性”,其构造方法可以看出:

1
2
3
4
Entry(Object key, V value,ReferenceQueue<Object> queue,int hash, Entry<K,V> next) {
super(key, queue); //这里比较关键
……
}

其中的super父类的代码实现为:

1
2
3
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}

通过对父类的构造方法可以知道,WeakMap的key值为弱引用类型,回顾一下弱引用的特点:垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。 于是WeakHashMap的作用就凸显出来了:对于数据中Key-value,key因为使用的弱引用会被回收,而value也会得到对应的释放。以我这次的需求为例:key为Webview页面或者Flutter/RN页面,value为调用native方法相关存储的对象。当页面需要关闭调webivew的时候,Webview应该要被释放,要不然会产生内存泄漏,当其被释放之后,对应的value也没有意义了,所以也需要被释放掉。

那么WeakHashMap是如何让value释放的呢?

二、WeakHashMap如何释放无用的Value

要回收无用的Value,那么引用队列(ReferenceQueue)就派上用场了,回顾一下引用队列的作用:当一个引用(软引用、弱引用)关联到了一个引用队列后,当这个引用所引用的对象要被垃圾回收时,就会将它加入到所关联的引用队列中。
所以判断一个引用对象是否已经被回收的一个现象就是,这个对象的引用是否被加入到了它所关联的引用队列。
那么对于WeakHashMap也是利用这一点特性,在其代码中put\get等方法都有执行对应等检查

1
2
3
4
5
public V put(K key, V value) {
Object k = maskNull(key);
int h = hash(k);
Entry<K,V>[] tab = getTable(); //具体实现在getTable执行的expungeStaleEntries里面
……
1
2
3
4
5
public V get(Object key) {
Object k = maskNull(key);
int h = hash(k);
Entry<K,V>[] tab = getTable();
……
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Expunges stale entries from the table.
*/
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null; ) { //这里的queue就是引用队列
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) x;
int i = indexFor(e.hash, table.length);

Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}

从上面的代码逻辑可以很清楚的知道:WeakHashMap通过对引用队列的数据进行检查,对key被回收对象的对应Value进行了回收。

三、总结

1、WeakHashMap的Entry 继承于WeakReference,并维护一个ReferenceQueue
2、在执行get\put等相关数据操作的时候 会对数据进行相关处理,主要是清除掉无用对象对