转载自: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