WeakHashMap是種弱引用的HashMap,這是說,WeakHashMap裡的key值如果沒有外部強引用,在垃圾回收之後,WeakHashMap的對應内容也會被移除掉。
在講解WeakHashMap之前,我們需要了解Java中引用的相關類:
ReferenceQueue,引用隊列,與某個引用類綁定,當引用死亡後,會進入這個隊列。
HardReference,強引用,任何以類似String str=new String()建立起來的引用,都是強引用。在str指向另一個對象或者null之前,該String對象都不會被GC(Garbage Collector垃圾回收器)回收;
WeakReference,弱引用,可以通過java.lang.ref.WeakReference來建立,當GC要求回收對象時,它不會阻止對象被回收,該對象會立刻被回收;
SoftReference,軟引用,可以通過java.lang.ref.SoftReference來建立,和弱引用一樣,當GC要求回收時,它不會阻止對象被回收,但不同的是該對回收過程會被延遲,必須要等到JVM heap記憶體不夠用,接近産生OutOfMemory錯誤時,才會回收;
PhantomReference,虛引用,可以通過java.lang.ref.PhantomPeference來建立,這種類型的引用很特别,大多數時間,無法通過它拿到其引用的對象,但是,當這個對象死亡的時候,該引用還是會進入ReferenceQueue隊列。
下面提供一個例子來分别說明它們的作用:
ReferenceQueue<Ref> queue = new ReferenceQueue<Ref>();
// 建立一個弱引用
WeakReference<Ref> weak = new WeakReference<Ref>(new Ref("Weak"),queue);
// 建立一個虛引用
PhantomReference<Ref> phantom = new PhantomReference<Ref>(new Ref(
"Phantom"), queue);
// 建立一個軟引用
SoftReference<Ref> soft = new SoftReference<Ref>(new Ref("Soft"),queue);
System.out.println("引用内容:");
System.out.println(weak.get());
System.out.println(phantom.get());
System.out.println(soft.get());
System.out.println("被回收的引用:");
for (Reference r = null; (r = queue.poll()) != null;) {
System.out.println(r);
}
Ref這個類是個自定義的測試類,源碼如下所示:
class Ref {
Object v;
Ref(Object v) {
this.v = v;
public String toString() {
return this.v.toString();
在這個例子裡,分别建立了弱引用、虛引用和軟引用,get()方法用于擷取它們引用的Ref對象,可以注意到,Ref對象在外部并沒有任何引用,是以,在某個時間點,GC應當會回收對象。來看看代碼執行的結果:
引用内容:
Weak
null
Soft
被回收的引用:
可以看到,弱引用和軟引用的對象還是可達的,但是虛引用是不可達的。被回收的引用沒有内容,說明GC還沒有回收它們。
這證明了虛引用的性質:
虛引用非常弱,以至于它自己也找不到自己的引用内容。
對之前的代碼進行改造,在輸出内容前加入代碼:
// 強制垃圾回收
System.gc();
再執行一次,得到結果:
java.lang.ref.WeakReference@3b764bce
java.lang.ref.PhantomReference@759ebb3d
現在可達的引用隻剩下Soft了,引用隊列裡多出了兩條引用,說明WeakReference和PhantomReference的對象被回收。
再修改一次代碼,讓WeakPeference和PhantomReference去引用一個強引用對象:
Ref wr = new Ref("Hard");
WeakReference<Ref> weak = new WeakReference<Ref>(wr, queue);
PhantomReference<Ref> phantom = new PhantomReference<Ref>(wr, queue);
輸出結果如下所示:
Hard
這證明了弱引用的性質:
弱引用的對象,如果沒有被強引用,在垃圾回收後,引用對象會不可達。
WeakHashMap利用了ReferenceQueue和WeakReference來實作它的核心功能:當key值沒有強引用的時候,從WeakHashMap裡移除。
先來看看WeakHashMap的鍵值對實體類WeakHashMap.Entry的實作:
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
...
關鍵注意兩處:
1、Entry繼承自WeakReference;
2、Entry本身沒有儲存key值,而是把key作為WeakReference的引用對象交給了super構造。
這說明,Entry是個針對key值的弱引用。
WeakHashMap實作清除陳舊實體的方法是expungStaleEntries(),其源碼實作如下:
private void expungeStaleEntries() {
//周遊引用隊列,找到每一個被GC收集的對象
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;
//幫助GC執行
e.value = null;
size--;
break;
}
prev = p;
p = next;
}
}
}
expungStaleEntries()方法會在resize、put、get、forEach方法裡被調用。