阅读时间:2019.1.28
一、概述
WeakHashMap 继承于AbstractMap,实现了Map接口。和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。
与HashMap不同的是,WeakHashMap 桶内一直是单链表存储,不进行红黑树转化。
最后,WeakHashMap 最关键的特性:key是弱键。
特征 | 举例 | |
---|---|---|
强引用 | 任何时候都不会被垃圾回收器回收,如果内存不足,宁愿抛出OutOfMemoryError | Object obj = new Object() |
软引用 | 只有在内存将满的时候才会被垃圾回收器回收,如果还有可用内存,垃圾回收器不会回收 | SoftReference sr = new SoftReference(new String(“hello”)); |
弱引用 | 只要垃圾回收器运行,就肯定会被回收,不管还有没有可用内存 | WeakReference sr = new WeakReference(new String(“hello”)); |
虚引用 | 等于没有引用,任何时候都有可能被垃圾回收 | PhantomReference pr = new PhantomReference(new String(“hello”), queue); |
所有,当WeakHashMap 中某个“弱键”不再被其它对象引用,在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。
二、主要属性和方法
实现弱引用的关键有两个,一个是特殊的Entry,一个是ReferenceQueue的 queue
Entry<K,V>[] table;
private int threshold;
private final float loadFactor;
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();//存放下次对map操作时,需要清除掉的节点
private static final Object NULL_KEY = new Object();
WeakHashMap 的节点是继承WeakReference的,所以从他的定义就是一个弱引用的,从而实现“当没有外部变量指向该节点时,就会把该节点放到队列中,等待下次GC是删除掉”
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);//初始化时,因为还没有别人来引用这个节点,就放到queue中,下次操作map时
//若还没有别人引用它,就把这个节点置空,以便GC
this.value = value;
this.hash = hash;
this.next = next;
}
public boolean equals(Object o) {
//...省略代码
}
public int hashCode() {
//...省略代码
}
public String toString() {
//...省略代码
}
}
WeakHashMap是主要通过expungeStaleEntries
函数的来实现移除其内部不用的entry从而达到的自动释放内存的目的。所有的WeakHashMap操作都会先进行移除不使用节点的方法
private Entry<K,V>[] getTable() {
expungeStaleEntries();
return table;
}
具体分析expungeStaleEntries
方法:
这个方法并不复杂:
第一步:先遍历queue,把queue中的节点在找到hashMap中找到
第二步:把这个节点的prev和next连接起来,把该节点=null
private void expungeStaleEntries() {
//第一步:先遍历queue,把queue中的节点在找到hashMap中找到
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;
//第二步:把这个节点的prev和next连接起来,把该节点=null
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
三、总结
初始化方法,扩容方法,操作方法斗鱼hashmap没有太大区别,所以不再复述。
通过对expungeStaleEntries方法学习,我们可以知道WeakHashMap的工作流程:
1)初始化,把新建的entry放到queue中;
2)每次对map进行操作时,先同步的检查queue中的是否有节点,有节点,说明这个节点没有被外部变量引用。该节点就会被置为null。
3)在下次GC时,便会把他删除掉