WeakHashMap 总结

发表于: java/j2ee | 作者: darkmi | 日期: 2014/01/02 08:00
字号:

WeakHashMap,此种Map的特点是,当除了自身有对key的引用外,此key没有其他引用那么此map会自动丢弃此值。

见实例:此例子中声明了两个Map对象,一个是HashMap,一个是WeakHashMap,同时向两个map中放入a、b两个对象,当HashMap remove掉a 并且将a、b都指向null时,WeakHashMap中的a将自动被回收掉。出现这个状况的原因是,对于a对象而言,当HashMap remove掉并且将a指向null后,除了WeakHashMap中还保存a外已经没有指向a的指针了,所以WeakHashMap会自动舍弃掉a,而对于b对象虽然指向了null,但HashMap中还有指向b的指针,所以WeakHashMap将会保留。

public class WeakMapStudy {
	public static void main(String[] args) throws Exception {
		String a = new String("a");
		String b = new String("b");
		Map<string, String> weakmap = new WeakHashMap<string, String>();
		Map<string, String> map = new HashMap<string, String>();
		map.put(a, "aaa");
		map.put(b, "bbb");
		weakmap.put(a, "aaa");
		weakmap.put(b, "bbb");
		map.remove(a);
		a = null;
		b = null;
		System.gc();
		Iterator<map.Entry<string, String>> i = map.entrySet().iterator();
		while (i.hasNext()) {
			Map.Entry<string, String> en = (Map.Entry<string, String>) i.next();
			System.out.println("map:" + en.getKey() + ":" + en.getValue());
		}
		Iterator<map.Entry<string, String>> j = weakmap.entrySet().iterator();
		while (j.hasNext()) {
			Map.Entry<string, String> en = (Map.Entry<string, String>) j.next();
			System.out.println("weakmap:" + en.getKey() + ":" + en.getValue());
		}
	}
}

[来源:http://mzlly999.iteye.com/blog/1126049\]

=================

在《Effective Java》一书中第六条,消除陈旧对象时,提到了 WeakHashMap,看了下还是适用的,即在我们使用短时间内就过期的缓存时最好使用WeakHashMap,它包含了一个自动调用的方法 expungeStaleEntries,这样就会在值被引用后直接执行这个隐含的方法,将不用的键清除掉。

测试了一下

package com.alibaba.itbu.job.billing;
import java.util.Map;
import java.util.WeakHashMap;
public class WeakHashMapTest {
    static Map wMap = new WeakHashMap();
    public static void init(){
        wMap.put("1", "ding");
        wMap.put("2", "job");
    }
    public static void testWeakHashMap(){
        System.out.println("first get:"+wMap.get("1"));
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("next get:"+wMap.get("1"));
    }
    public static void main(String[] args) {
        testWeakHashMap();
    }
}

上面例子, 第一次执行时要初始化,然后在5s内是不会清除的,大概在10几秒时会清除

第一次执行

first get:ding

next get:ding

过一会再执行:

first get:null

next get:null

这时候已经被清除

同样,没有调用任何赋值方法的情况下,在一段时间后 size 方法也可能返回较小的值,对于 isEmpty 方法,返回false,然后返回 true,对于给定的键,containsKey 方法返回 true 然后返回 false,对于给定的键,get 方法返回一个值,但接着返回 null,对于以前出现在映射中的键,put 方法返回 null,而 remove 方法返回 false,对于键集、值集、项集进行的检查,生成的元素数量越来越少。

注意:WeakHashMap并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象。这两句话看似区别不大,但是有时候一个小小的区别就会要了命的。就是说你只put 了压根没有get过,这个值是永远都存在的

我们也可以看下这个移除键值的实现

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;
            }
        }
    }

就是使用了链表,找到这个hash值 将这个hash值移除 size减少

[来源:http://www.cnblogs.com/jisheng/archive/2011/12/02/2271862.html]

=================

在广大的Java界,关于WeakHashMap一直都存在这么个传说:

在WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目

在WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目 可是WeakHashMap是真的自动移除其条目吗?

今天因为闲来无事,所以想看看WeakHashMap是如何自动实现移除其内部不用的条目从而达到的自动释放内存的目的的。仔细的看了看JVM自带的源代码的实现,在WeakHashMap是主要通过expungeStaleEntries这个函数的来实现的。基本上只要对WeakHashMap的内容进行访问就会调用这个函数,从而达到清除其内部不在为外部引用的条目。但是如果预先生成了WeakHashMap,而在GC以前又不曾访问该WeakHashMap,那不是就不能释放内存了吗?

写个代码测试一把:

public static void main(String[] args) throws Exception {
	List<weakHashMap<byte[][], byte[][]>> maps = new ArrayList<weakHashMap<byte[][], byte[][]>>();
	for (int i = 0; i < 1000; i++) {
		WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
		d.put(new byte[1000][1000], new byte[1000][1000]);
		maps.add(d);
		System.gc();
		System.err.println(i);
	}
}

由于Java默认内存是64M,所以再不改变内存参数的情况下,该测试跑不了几步循环就内存溢出了。果不其然,WeakHashMap这个时候并没有自动帮我们释放不用的内存。

再加个对会对map进行访问的测试试试:

public static void main(String[] args) throws Exception {
		List<weakHashMap<byte[][], byte[][]>> maps = new ArrayList<weakHashMap<byte[][], byte[][]>>();
		for (int i = 0; i < 1000; i++) {
			WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>();
			d.put(new byte[1000][1000], new byte[1000][1000]);
			maps.add(d);
			System.gc();
			System.err.println(i);
			for (int j = 0; j < i; j++) {
				System.err.println(j+  " size" + maps.get(j).size());
			}
		}
	}

这下测试就顺利通过了。

总结来说:WeakHashMap并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象。这两句话看似区别不大,但是有时候一个小小的区别就会要了命的。

来源:http://www.iteye.com/topic/587995

切换主题切换主题
返回顶部