WeakHashMap和Java引用类型详细解析
WeakHashMap是種弱引用的HashMap,這是說,WeakHashMap里的key值如果沒有外部強引用,在垃圾回收之后,WeakHashMap的對應(yīng)內(nèi)容也會被移除掉。
1.1?Java的引用類型
在講解WeakHashMap之前,我們需要了解Java中引用的相關(guān)類:
ReferenceQueue,引用隊列,與某個引用類綁定,當(dāng)引用死亡后,會進入這個隊列。
HardReference,強引用,任何以類似String str=new String()建立起來的引用,都是強引用。在str指向另一個對象或者null之前,該String對象都不會被GC(Garbage Collector垃圾回收器)回收;
WeakReference,弱引用,可以通過java.lang.ref.WeakReference來建立,當(dāng)GC要求回收對象時,它不會阻止對象被回收,該對象會立刻被回收;
SoftReference,軟引用,可以通過java.lang.ref.SoftReference來建立,和弱引用一樣,當(dāng)GC要求回收時,它不會阻止對象被回收,但不同的是該對回收過程會被延遲,必須要等到JVM heap內(nèi)存不夠用,接近產(chǎn)生OutOfMemory錯誤時,才會回收;
PhantomReference,虛引用,可以通過java.lang.ref.PhantomPeference來建立,這種類型的引用很特別,大多數(shù)時間,無法通過它拿到其引用的對象,但是,當(dāng)這個對象死亡的時候,該引用還是會進入ReferenceQueue隊列。
下面提供一個例子來分別說明它們的作用:
| ReferenceQueue<Ref> queue?= new?ReferenceQueue<Ref>(); // 創(chuàng)建一個弱引用 WeakReference<Ref> weak?= new?WeakReference<Ref>(new?Ref("Weak"),queue); // 創(chuàng)建一個虛引用 PhantomReference<Ref> phantom?= new?PhantomReference<Ref>(new?Ref( "Phantom"), queue); // 創(chuàng)建一個軟引用 SoftReference<Ref> soft?= new?SoftReference<Ref>(new?Ref("Soft"),queue); ? System.out.println("引用內(nèi)容:"); 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(); } } |
?
在這個例子里,分別創(chuàng)建了弱引用、虛引用和軟引用,get()方法用于獲取它們引用的Ref對象,可以注意到,Ref對象在外部并沒有任何引用,所以,在某個時間點,GC應(yīng)當(dāng)會回收對象。來看看代碼執(zhí)行的結(jié)果:
| 引用內(nèi)容: Weak null Soft 被回收的引用: |
可以看到,弱引用和軟引用的對象還是可達的,但是虛引用是不可達的。被回收的引用沒有內(nèi)容,說明GC還沒有回收它們。
這證實了虛引用的性質(zhì):
虛引用非常弱,以至于它自己也找不到自己的引用內(nèi)容。
對之前的代碼進行改造,在輸出內(nèi)容前加入代碼:
| // 強制垃圾回收 System.gc(); |
再執(zhí)行一次,得到結(jié)果:
| 引用內(nèi)容: null null Soft 被回收的引用: java.lang.ref.WeakReference@3b764bce java.lang.ref.PhantomReference@759ebb3d |
現(xiàn)在可達的引用只剩下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); |
輸出結(jié)果如下所示:
| 引用內(nèi)容: Hard null Soft 被回收的引用: |
這證實了弱引用的性質(zhì):
弱引用的對象,如果沒有被強引用,在垃圾回收后,引用對象會不可達。
?
1.2?WeakHashMap實現(xiàn)方式
WeakHashMap利用了ReferenceQueue和WeakReference來實現(xiàn)它的核心功能:當(dāng)key值沒有強引用的時候,從WeakHashMap里移除。
先來看看WeakHashMap的鍵值對實體類WeakHashMap.Entry的實現(xiàn):
| ?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; ????????} ????... } |
關(guān)鍵注意兩處:
1、Entry繼承自WeakReference;
2、Entry本身沒有保存key值,而是把key作為WeakReference的引用對象交給了super構(gòu)造。
這說明,Entry是個針對key值的弱引用。
WeakHashMap實現(xiàn)清除陳舊實體的方法是expungStaleEntries(),其源碼實現(xiàn)如下:
| 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執(zhí)行 ????????????????????????e.value?= null; ????????????????????????size--; ????????????????????????break; ????????????????????} ????????????????????prev?= p; ????????????????????p?= next; ????????????????} ????????????} ????????} ????} |
?
expungStaleEntries()方法會在resize、put、get、forEach方法里被調(diào)用。
總結(jié)
以上是生活随笔為你收集整理的WeakHashMap和Java引用类型详细解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#和C++结构体Socket通信
- 下一篇: Java 9进入第一轮问题修复阶段