WeakHashMap冲簿,它充分利用了WeakReference弱引用的特性涣旨,適合內存敏感的緩存實現(xiàn)場景氏仗。今天簡單扒一扒它的實現(xiàn)原理。
首先看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的時候催烘,還有一個特別的參數(shù)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()的清理動作在哪些時候會被觸發(fā)呢丸卷?
> 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的操作都會觸發(fā)清理。