WeakHashMap是種弱引用的HashMap,這是說,WeakHashMap里的key值如果沒有外部強引用,在垃圾回收之后,WeakHashMap的對應內容也會被移除掉。
1.1?Java的引用類型
在講解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(); |
再執行一次,得到結果:
引用內容: null null Soft 被回收的引用: 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 null Soft 被回收的引用: |
這證實了弱引用的性質:
弱引用的對象,如果沒有被強引用,在垃圾回收后,引用對象會不可達。
?
1.2?WeakHashMap實現方式
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方法里被調用。