集合源码学习(九):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
相关文章推荐
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
- 【Java集合学习系列】HashMap实现原理及源码分析
- 深入Java集合学习系列:WeakHashMap的实现原理
- java集合13--WeakHashMap源码详解
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
- 集合源码学习(七):HashMap(Java8)
- Java集合源码学习笔记(四)HashMap分析
- java_集合体系之WeakHashMap详解、源码及示例——11
- 深入Java集合学习系列:WeakHashMap的实现原理
- Java 8集合框架源码学习——HashMap
- JAVA学习18_Java集合---HashMap源码剖析
- Java集合源码学习(24)_ConcurrentMap的实现类ConcurrentHashMap
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
- Java集合源码学习(20)_Map接口的实现HashMap
- Java集合ArrayList和HashMap源码学习
- 集合源码学习(十):HashTable(Java8)与HashMap比较
- Java集合源码学习笔记(五)ArrayList,LinkedList,Vector和Hashtable,HashMap的比较
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理
- 深入Java集合学习系列:HashMap的实现原理