WeakHashMap粗解

    xiaoxiao2021-03-25  100

    转载自:http://blog.csdn.net/u010412719/article/details/52035792               http://blog.csdn.net/z69183787/article/details/54581740               http://blog.csdn.net/z69183787/article/details/54582249       首先得了解Reference与ReferenceQueue,可以参看:http://blog.csdn.net/zero__007/article/details/77984272        WeakHashMap 继承于AbstractMap,实现了Map接口。 和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。       WeakHashMap的键是“弱键”,里面存放了键对象的弱引用,当某个键不再正常使用时,会从WeakHashMap中被自动移除。当一个键对象被垃圾回收,那么相应的值对象的引用会从Map中删除。WeakHashMap能够节约存储空间,可用来缓存那些非必须存在的数据。        WeakHashMap的“弱键”就是用WeakReference、ReferenceQueue来实现的。WeakHashMap内部的Entry<K,V>源码如下: /** * The entries in this hash table extend WeakReference, using its main ref * field as the key. */ private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { V value; final int hash; Entry<K,V> next; /** * Creates new entry. */ Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K,V> next) { super(key, queue);// WeakReference(referent, q) this.value = value; this.hash = hash; this.next = next; } @SuppressWarnings("unchecked") public K getKey() { return (K) WeakHashMap.unmaskNull(get());//Reference.get() } public V getValue() { return value; } ……       在WeakHashMap中,key都是WeakReference类型,在WeakHashMap.expungeStaleEntries方法中: /** * Expunges stale entries from the table. */ private void expungeStaleEntries() { for (Object x; (x = queue.poll()) != null; ) { 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) { Entry<K,V> next = p.next; if (p == e) { if (prev == e) table[i] = next; else prev.next = next; // Must not null out e.next; // stale entries may be in use by a HashIterator e.value = null; // Help GC size--; break; } prev = p; p = next; } } } }       也许会有个疑问,明明Entry 使用 WeakReference super方法初始化时,queue中加入的是key对象,为何poll的时候对象x的类型为Entry<X,Y>?这是因为key的引用发生变化后(没有再被引用),reference实例会进入Pending状态,之后ReferenceHandler线程会将进入Pending状态的reference,调用q.enqueue()方法,加入到queue中,那么queue.poll()的时候,返回的即reference,也就是Entry<X,Y>对象。        通过对queue的遍历,获取所有需要清除的“弱引用key”,然后再WeakHashMap中删除对应键值对。        需要注意,WeakHashMap的Key是弱引用,Value不是。WeakHashMap不会自动释放失效的弱引用,仅当包含了expungeStaleEntries()的共有方法被调用的时候才会释放。简单示例: public static void main(String args[]) { WeakHashMap<String, String> map = new WeakHashMap<String, String>(); map.put(new String("1"), "1"); map.put("2", "2"); String s = new String("3"); map.put(s, "3"); while (map.size() > 0) { try { Thread.sleep(500); } catch (InterruptedException ignored) { } System.out.println("Map Size:" + map.size()); System.out.println(map.get("1")); System.out.println(map.get("2")); System.out.println(map.get("3")); System.gc(); } }       通过运行结果会发现,map.get("1")对应的value会被GC,因为new String("1")没有被引用,map.get("2")对应的key被放在常量池中,map.get("3")对应的key还有s在引用。        WeakHashMap是主要通过expungeStaleEntries这个函数的来实现移除其内部不用的Entry从而达到的自动释放内存的目的的。基本上只要对WeakHashMap的内容进行访问就会调用这个函数,从而达到清除其内部不在为外部引用的Entry。但是如果生成了WeakHashMap,而在GC以前又不曾访问该WeakHashMap,那不是就不能释放内存了吗?        对应的两个测试案例: 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); } 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()); } }       第一个例子会会造成OOM异常,而第二个不会。因为size()里面触发了expungeStaleEntries操作,它将 ReferenceQueue中的 WeakReference对象从map中删除了,对应着value也一并删除了,使得value也被GC回收了。        总结来说:WeakHashMap并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象
    转载请注明原文地址: https://ju.6miu.com/read-15119.html

    最新回复(0)