WeakHashMap的Key-Value回收原理
2018-03-30 本文已影响0人
徐志毅
WeakHashMap,它充分利用了WeakReference弱引用的特性,适合内存敏感的缓存实现场景。今天简单扒一扒它的实现原理。
首先看WeakHashMap的Entry定义:
//继承WeakReference
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
int hash;
Entry<K,V> next;
/**
* Creates new entry.
*/
Entry(Object key, V value, ReferenceQueue<Object> queue, int hash, Entry<K,V> next) {
//这个super很关键,调用WeakReference的构造方法,使用key做referent
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
@SuppressWarnings("unchecked")
public K getKey() {
return (K) WeakHashMap.unmaskNull(get());
}
//....其他省略
}
Entry继承WeakReference,使用key作为WeakReference的弱引用,这意味着只要key没有其他地方持有(Map外不可达),map中的key就会被回收。目前来看,暂时只能回收key,那value是如何被回收的呢?在构造Entry的时候,还有一个特别的参数queue,这是WeakHashMap的一个成员变量:
/**
* Reference queue for cleared WeakEntries
* 被清理的WeakEntries队列
*/
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
有了这个队列,map就知道哪些key被GC回收掉了,这样就有办法控制value的回收了。
/**
* Returns the table after first expunging stale entries.
*/
private Entry<K,V>[] getTable() {
expungeStaleEntries();
return table;
}
/**
* Expunges stale entries from the table.
* 清理掉被GC的key对应Entries
*/
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;
}
}
}
}
实际上value的回收是延迟与key的,仅仅是在key被GC后,把value置为null,并不是立即回收。
下面看一下expungeStaleEntries()的清理动作在哪些时候会被触发呢?
> expungeStaleEntries()
> size()
> getTable()
> containsNullValue()
> forEach(BiConsumer<? super K, ? super V> action)
> get(Object key)
> getEntry(Object key)
> put(K key, V value)
> remove(Object key)
> removeMapping(Object o)
> replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
> resize(int newCapacity)
从这些方法来看,基本上我们对WeakHashMap的操作都会触发清理。