您的位置:首页 > 编程语言 > Java开发

集合源码学习(九):WeakHashMap(Java8)关于key

2017-10-17 23:37 656 查看

什么是WeakHashMap?

WeakHashMap是以及基于java弱引用实现的HashMap,感觉一句话就讲的差不多了嘿嘿。

先看看它的定义:

public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>


如上,在定义方面和HashMap并没有什么不同。而且在结构上,基本和HashMap一致。在Java8中,唯一存储上的不同点就是,当冲突的key变多时,HashMap引入了二叉树(红黑树)进行存储,而WeakHashMap则一直使用链表进行存储。

而WeakHashMap的特点,这里也有总结:

基于map接口,是一种弱键相连,WeakHashMap里面的键会自动回收

支持 null值和null键。和HashMap有些相似

fast-fail机制

不允许重复

WeakHashMap经常用作缓存

关于HashMap的具体存储结构,这里就不细讲了,大家可以看我这篇深入分析的博客:集合源码学习:HashMap

Java里面引用分为4中类型,大点分有强,弱,

大家可以参阅我这篇文章结合源码详细介绍4种引用。Java里面引用分类

而在WeakHashMap则主要用到了WeakReference这个引用,这篇文章就主要分析,为什么WeakHashMap具有如此特性(主要是Weak)。

关于
Entry<K,V>

和HashMap一样,WeakHashMap也是用一个Entry实体来构造里面所有的元素的,但是这个Entry却和HashMap的不同,他是弱引用。

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V>


如上,Entry还继承了WeakReference,所以Entry是个弱引用。何为弱引用呢?就是就是每当进行一次gc,你这个对象就会被清除,当然如果这个对象还存在着软引用或者强引用,就可能不会被清除。

ReferenceQueue queue作用

queue是用来存放那些,被jvm清除的entry的引用,因为WeakHashMap使用的是弱引用,所以一旦gc,就会有key键被清除,所以会把entry加入到queue中。在WeakHashMap中加入queue的目的,就是为expungeStaleEntries所用。

Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash  = hash;
this.next  = next;
}


在构造每一个Entry时,都将它与queue绑定,从而一旦被jvm回收,那么这个Entry就会倍加如到queue中。

expungeStaleEntries方法具体意思

这里面我的理解和很大一部分的博客都不同,我认为在这个方法里面就仅仅是释放value值。由前面的Entry的构造方法可知,
super(key, queue);
传入父类的仅仅是key,所以经过仔细阅读jdk源码开始部分分析后,得出结论,在WeakHashMap中,有jvm回收的,仅仅是Entry的key部分,所以一旦jvm强制回收,那么这些key都会为null,再通过私有的
expungeStaleEntries
方法,把value也制null,并且把
size--


首先看代码:

/**
* 从ReferenceQueue中取出过期的entry,从WeakHashMap找到对应的entry,逐一删除
* 注意,只会把value置为null。
*/
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) {
//遍历table[i]所在链表
Entry<K,V> next = p.next;
if (p == e) {
//queue里面有e,那就删了。
if (prev == e)
//e就是当前的p.next
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
//置为null,帮助gc。只制null了value。
e.value = null; // Help GC
//设置e的value,但是没看到设置e的key。
size--;
break;
}
prev = p;
p = next;
}
}
}
}


上面代码逻辑为,当在table中找到queue中存在元素时,就把value制空,然后
size--
。所以在WeakHashMap中,就只有key被回收。下面看一个实例验证。

首先需要了解一点:expungeStaleEntries方法在哪些方面会被调用?

经过阅读源码,发现expungeStaleEntries方法只在一下几个地方被调用:

private Entry<K,V>[] getTable()
里面,而这个getTable则在下列方法被调用:

public V get(Object key)


Entry<K,V> getEntry(Object key)


public V put(K key, V value)


void resize(int newCapacity)


public V remove(Object key)


boolean removeMapping(Object o)


public boolean containsValue(Object value)


private boolean containsNullValue()


public void forEach(BiConsumer<? super K, ? super V> action)


public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)


public int size()


void resize(int newCapacity)


大致了解了上面调用情况后,现在看一段测试代码。

测试:

public static void main(String[] args) throws InterruptedException {
WeakHashMap<String, String> map = new WeakHashMap<String, String>();
for(int i = 0;i < 5;i++){
map.put(new String("字符串"+i), new String("串串"+i));
}
System.gc();
TimeUnit.SECONDS.sleep(2);
System.out.println(map.size());
map.put(new String("字符串"+6), new String("串串"+6));
}


当我在WeakHashMap源码的size方法里面打一个断点时候:



之后,程序直接掉进这里面,从这里面可以看出size大小以及table详情看下图:





所以,从上面可以看出,WeakHashMap中,key会被gc,而value,则是通过expungeStaleEntries赋值为null。

疑问

验证我的思维过程中,遇到了一个很神的问题:

我在测试过程中,真的是测了好多次代码,才发现这样可以得到我所推理出的情况,但是,当我把断点打到上面测试代码第8行时,size为0,并且table也完全为空。

后来我发现,不管在哪里打,只要你在你测试类打,然后再跳到源码去看,size还是为0,甚至我在expungeStaleEntries里面加一个断点,却无法被调用。

本来尝试把整个jdk项目放到eclipse里满去里面查找,但是内存耗不起,加载半天加载不出来。

如果哪位老哥遇到过或者解决过这个问题,请在下方留言帮助我解答这个疑惑吧,感激不尽。

花了好些天学习WeakHashMap,在学习中除了看源码也从下列博客中得到了很多知识:

http://blog.csdn.net/u013256816/article/details/50907595

http://blog.csdn.net/u013256816/article/details/50916504

http://www.cnblogs.com/selfchange/p/7154909.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: