WeakHashMap的回收机制分析

(转载)[https://www.ktanx.com/blog/p/288]

在Java中,一般人对于WeakHashMap的理解都是:当某个键不再被使用时,将自动移除回收其条目。

WeakHashMap真的是自动移除回收其条目的吗?

看了下WeakHashMap的实现源码,主要是通过expungeStaleEntries这个函数来实现回收的,基本上WeakHashMap中所有的public方法都调用了该函数,简言之就是只要访问WeakHashMap的内容就会调用该函数,从而达到清除其内部不再被引用的条目。

但是如果预先生成了WeakHashMap,而在gc以前又不曾访问该WeakHashMap,是不是就不能释放内存了呢? 写个代码测试一下:

List> maps = new ArrayList>();
for (int i = 0; i < 1000; i++) {
 WeakHashMap d = new WeakHashMap();
 d.put(new byte[5000][5000], new byte[5000][5000]);
 maps.add(d);
 System.gc();
 System.err.println(i);
}

在不改变JDK内存参数的情况下,该测试循环不了几次就内存溢出了,果不其然,WeakHashMap在这时候并没有自动帮我们清理不用的条目而释放内存。

加个会对map进行访问的代码试试:


List> maps = new ArrayList>();

for (int i = 0; i < 1000; i++) {

 WeakHashMap d = new WeakHashMap();

 d.put(new byte[1000][1000], new byte[1000][1000]);

 maps.add(d);

 System.gc();

 System.err.println(i);

 //元素并不存在,只达到访问WeakHashMap方法目的

 System.out.println(maps.get(0));

}

这下就没有问题了,测试顺利通过。

可见,WeakHashMap并不是你啥也不干就能自动清理回收内部对象的,而是在你访问它内容的时候释放内部不用的对象(实则是通过访问调用了它的expungeStaleEntries函数),没理解这一点,在程序中就有可能引发灾难!

在WeakHashMap$Entry的构造函数里面是这样写的:

Entry(K key, V value, ReferenceQueue queue, int hash, Entry next) {

 super(key, queue);

 this.value = value;

 this.hash = hash;

 this.next = next;

}

注意它构造父类的语句:super(key, queue),传入的是key,因此key才是进行弱引用的,value是直接强引用关联在this.value之中,在System.gc()时,key被清除了,WeakHashMap本身根据ReferenceQueue中接收到的清除通知来清理value值,这个动作实现在expungeStaleEntries()方法之内,在getTable()之中对这个方法进行了调用,而WeakHashMap几乎在所有public的方法中都是要调用getTable()的。所以效果是key在GC的时候被清除,value在key清除后访问WeakHashMap的时候被清除。

WeakHashMap的说明之中也是说“An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use”。所以WeakHashMap的实现并没有问题,只是人们大多想当然的理解为value会自动清除而已。

你可能感兴趣的:(Android,Framework)