Java的弱引用—WeakHashMap
Posted 十木禾
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java的弱引用—WeakHashMap相关的知识,希望对你有一定的参考价值。
在《Effective Java》中的p23页有涉及到WeakHashMap
的相关知识,在这篇文章中做一个总结以及介绍一下相关知识。
在这里我们分成三个部分来说明一下,这只是我自己参看JDK源码和上网搜索资料得到的结果,如有错误,欢迎指出,我不胜荣幸。
WeakHashMap和HashMap有什么不同
我们知道WeakHashMap
是弱引用,而HashMap
是强引用。
这就是说当我们给Java虚拟机分配的内存不足的时候,HashMap
宁可抛出OutOfMemoryError
异常也不会回收其相应的没有被引用的对象,而我们的WeakHashMap
则会回收存储在其中但有被引用的对象。
如下的程序实例,运用的JVM参数为:-Xmx5m -Xms5m -XX:+PrintGC
HashMap
的测试代码
public static void main(String[] args)
HashMap hashMap = new HashMap();
for (int i = 0; ; i++)
hashMap.put(i, new String("HashMap"));//一直往里面加数据
if (i % 1000 == 0) //每隔一千次判断一下有没有对象被回收
for (int j = 0; j < i; j++) //遍历一遍
if (hashMap.get(j) == null)
System.out.println("第" + j + "个对象开始回收");
return;
我们运行程序,发现没有任何对象被回收,最后抛出了异常
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.HashMap.addEntry(HashMap.java:753)
at java.util.HashMap.put(HashMap.java:385)
那我们再来证明一下WeakHashMap
会回收其中存储的没有被引用的对象
public static void main(String[] args)
WeakHashMap hashMap = new WeakHashMap();
for (int i = 0; ; i++)
hashMap.put(i, new String("WeakHashMap"));//一直往里面加数据
if (i % 1000 == 0) //每隔一千次判断一下有没有对象被回收
for (int j = 0; j < i; j++) //遍历一遍
if (hashMap.get(j) == null)
System.out.println("第" + j + "个对象开始回收");
return;
最后程序的输出如下
[GC 1408K->787K(4992K), 0.0067048 secs]
第241个对象开始回收
可知WeakHashMap
将其中存储的键为241的对象开始回收了。
我们接下来思考,WeakHashMap
是怎样知道要回收对象的呢?
WeakHashMap
通过将一些没有被引用的键的值赋值为null
,这样的话就会告知GC去回收这些存储的值了。
那么也就是说,如果我们将其所有的键都添加引用,那么其就不会被回收了?我们来写一段代码测试一下
public static void main(String[] args)
WeakHashMap weakHashMap = new WeakHashMap();
HashMap hashMap=new HashMap();
for (int i = 0; ; i++)
Integer num=new Integer(i);
hashMap.put(i,num); // num 被引用
weakHashMap.put(num, new String("WeakHashMap"));//将num改为i就会有对象被回收
if (i % 1000 == 0) //每隔一千次判断一下有没有对象被回收
for (int j = 0; j < i; j++) //遍历一遍
if (weakHashMap.get(j) == null)
System.out.println("第" + j + "个对象开始回收");
return;
我们在HashMap
中存储了WeakHashMap
的键,这样就会是的其不会被回收,最后测试结果表示没有对象被回收,程序也就像我们期待的一下报了OutOfMemoryError
。
当然可能会有人说,为什么这个OutOfMemoryError
不是我们的HashMap
引起的呢,然后WeakHashMap
还没有达到回收的条件?
针对这一点,我根据程序中的注释将WeakHahMap
的键从num改为i,最后会发现有对象被回收。
既然WeakHashMap
是将我们的键值设置为null
从而引起GC的,那么我们将null
作为键存进去,为什么不会导致被回收呢?
这时候我们就需要看看其JDK的有关put
方法的源码了
public V put(K key, V value)
K k = (K) maskNull(key);// 重点看这里
int h = HashMap.hash(k.hashCode());
Entry[] tab = getTable();
int i = indexFor(h, tab.length);
for (Entry<K,V> e = tab[i]; e != null; e = e.next)
if (h == e.hash && eq(k, e.get()))
V oldValue = e.value;
if (value != oldValue)
e.value = value;
return oldValue;
modCount++;
Entry<K,V> e = tab[i];
tab[i] = new Entry<K,V>(k, value, queue, h, e);
if (++size >= threshold)
resize(tab.length * 2);
return null;
我们重点看一下开始的判断键时候为null
的maskNull()
方法。
private static Object maskNull(Object key)
return (key == null ? NULL_KEY : key);
我们发现,如果key
是null
的话,返回的是NULL_KEY
这个静态值,我们再来看一下这个值是什么的时候,就恍然大悟了
/**
* Value representing null keys inside tables.
*/
private static final Object NULL_KEY = new Object();
WeakHashMap
在存储null
为键的时候,其实存储的是其本身的静态成员变量Object
,也就是说存储的不是null
。
这也就解释了为什么存储键为null
不会被马上回收。
最后我们来看一下程序是在什么时候来判断要将没有引用的key
标记为null
的呢?
这时候WeakHashMap
中的一个非常重要的方法expungeStaleEntries()
就登场了。
在WeakHashMap
的put()
,get()
,remove()
等等方法中都调用了一个getTable()
方法,而这个getTable()
方法的源码如下:
private Entry[] getTable()
expungeStaleEntries();
return table;
可以知道他们其实调用的都是expungeStaleEntries()
方法。
可知这个方法是一个非常重要的方法。
[注].出自: Java集合框架:WeakHashMap,一篇非常优秀的博客!
首先我们看一下其实现的源码
/**
* Expunges stale entries from the table.
*/
private void expungeStaleEntries()
Entry<K,V> e;
while ( (e = (Entry<K,V>) queue.poll()) != null)
int h = e.hash;
int i = indexFor(h, 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;
e.next = null; // Help GC
e.value = null; // " "
size--;
break;
prev = p;
p = next;
可以看到每调用一次expungeStaleEntries()
方法,就会在引用队列中寻找是否有将要被清除的key
对象,如果有则在table中找到其值,并将value
设置为null
,next
指针也设置为null
,让GC去回收这些资源。
2017-11-18 15:23 于上海
以上是关于Java的弱引用—WeakHashMap的主要内容,如果未能解决你的问题,请参考以下文章
JAVA中的四种引用以及ReferenceQueue和WeakHashMap的使用示例