1. 程式人生 > >【轉載】 深入理解WeakHashmap

【轉載】 深入理解WeakHashmap

轉載出處

WeakHashmap

(一) 檢視API文件,WeakHashmap要點如下:

  1. 以弱鍵 實現的基於雜湊表的 Map。在 WeakHashMap 中,當某個鍵不再正常使用時,將自動移除其條目。更精確地說,對於一個給定的鍵,其對映的存在並不阻止垃圾回收器對該鍵的丟棄,這就使該鍵成為可終止的,被終止,然後被回收。丟棄某個鍵時,其條目從對映中有效地移除

  2. WeakHashMap 類的行為部分取決於垃圾回收器的動作。因為垃圾回收器在任何時候都可能丟棄鍵,WeakHashMap 就像是一個被悄悄移除條目的未知執行緒。特別地,即使對 WeakHashMap 例項進行同步,並且沒有呼叫任何賦值方法,在一段時間後 size 方法也可能返回較小的值,對於 isEmpty 方法,返回 false,然後返回true,對於給定的鍵,containsKey 方法返回 true 然後返回 false,對於給定的鍵,get 方法返回一個值,但接著返回 null,對於以前出現在對映中的鍵,put 方法返回 null,而 remove 方法返回 false,對於鍵 set、值 collection 和條目 set 進行的檢查,生成的元素數量越來越少。

  3. WeakHashMap 中的每個鍵物件間接地儲存為一個弱引用的指示物件。因此,不管是在對映內還是在對映之外,只有在垃圾回收器清除某個鍵的弱引用之後,該鍵才會自動移除。

(二) 引用物件的四種分類:

Api圖片如下:
Api圖片
1. 強引用(Strong Reference)

  1. 弱引用(Weak Reference)

  2. 軟引用(Soft Reference)

  3. 幻象引用(Phantom Reference)

(三) 深入理解

至此,對於WeakHashMap有了一個基本概念,但是還是比較模糊。找到一篇英文文件,將要點總結如下(如有翻譯不對,請指出,謝謝):

  1. 強引用

強引用就是普通的Java引用,程式碼:

StringBuffer buffer = new StringBuffer(); 

這句程式碼建立了一個StringBuffer物件,在變數buffer中儲存了這個物件的一個強引用。強引用之所以稱之為“強”(Strong),是因為他們與垃圾回收器(garbage collector)互動的方式。特別是(specifically),如果一個物件通過強引用連線(strongly reachable-強引用可到達),那麼它就不在垃圾回收期處理之列。當你正在使用某個物件而不希望垃圾回收期銷燬這個物件時,強引用通常正好能滿足你所要的。

  1. 當強引用太強的時候

假定你要使用一個final類Widget,但是基於某種原因,你不能繼承(extend)這個類或者通過其他方法為這個類增加一些新的功能。如果你需要跟蹤(track)這個類的不同物件的序列號。那麼可以將這些物件放入HashMap中,獲得不同的value值,這樣就可以做到通過不同的value值跟蹤這些物件了。程式碼:

serialNumberMap.put(widget, widgetSerialNumber); 

但是widget的強引用會產生一些問題。在我們不需要一個Widget的序列號時,我們需要將這個Widget對應的Entry從HashMap中移除。否則我們可能面臨記憶體洩露(memory leak)的問題(如果我們沒有在應當移除Widgt的時候移除它),或者我們將莫名其妙的丟失序列號(如果我們正在使用Widget時卻移除了它)。如果這些問題類似,那麼這些問題是無垃圾回收機制(non-garbage-collected language)的程式語言的開發者所面臨的問題。Java的開發者不需要擔心這種問題。

W:如果一個物件具有強引用,那就類似於必不可少的生活用品,垃圾回收器絕不會回收它。當記憶體空 間不足,Java虛擬機器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足問題。

強引用的另外一個常見問題就是圖片快取(cache)。普通的強引用將使得Image繼續儲存在記憶體中。在一些情況下,我們不需要有些Image繼續留在記憶體中,我們需要將這些圖片從記憶體中移出,這時,我們將扮演垃圾回收期的角色來決定哪些照片被移除。使這些被移出的圖片被垃圾回收器銷燬。下一次,你被迫再次扮演垃圾回收期的角色,手動決定哪些Image被回收。

Note:我覺得也可以用物件的hashCode來跟蹤物件。作者在此所舉的例子,只在說明Strong Reference。

  1. 弱引用

簡單說,就是弱引用不足以將其連線的物件強制儲存在記憶體中。弱引用能夠影響(leverage)垃圾回收器的某個物件的可到達級別。程式碼:

WeakReference<Widget> weakWidget = new WeakReference<Widget>(widget); 

你可以使用weakWidget.get()方法老獲取實際的強引用物件。但是在之後,有可能突然返回null值(如果沒有其他的強引用在Widget之上),因為這個弱引用被回收。其中包裝的Widget也被回收。

解決Widget序列號的問題,最簡單的方法就是使用WeakHashmap。其key值為弱引用。如果一個WeakHashmap的key變成垃圾,那麼它對應用的value也自動的被移除。

W:垃圾回收期並不會總在第一次就找到弱引用,而是會找幾次才能找到。

  1. 引用佇列(Reference Quene)

一旦弱引用返回null值,那麼其指向的物件(即Widget)就變成了垃圾,這個弱引用物件(即weakWidget)也就沒有用了。這通常意味著要進行一定方式的清理(cleanup)。例如,WeakHashmap將會移除一些死的(dread)的entry,避免持有過多死的弱引用。

ReferenceQuene能夠輕易的追蹤這些死掉的弱引用。可以講ReferenceQuene傳入WeakHashmap的構造方法(constructor)中,這樣,一旦這個弱引用指向的物件成為垃圾,這個弱引用將加入ReferenceQuene中。

如下圖所示:
--

  1. 軟引用

除了在丟擲自己所指向的物件的迫切程度方面不一樣之外,軟引用和弱引用基本一樣。一個物件為弱可到達(或者指向這個物件的強引用是一個弱引用物件-即強引用的弱引用封裝),這個物件將在一個垃圾回收迴圈內被丟棄。但是,弱引用物件會保留一段時間之後才會被丟棄。

軟引用的執行和弱引用並沒有任何不同。但是,在供應充足(in plentiful supply)的情況下,軟可到達物件將在記憶體中儲存儘可能長的時間。這使得他們在記憶體中有絕佳的存在基礎(即有儘可能長存在的基礎)。因為你讓垃圾回收器去擔心兩件事情,一件是這個物件的可到達性,一件是垃圾回收期多麼想要這些物件正在消耗的記憶體。

  1. 幻象引用(phantom reference)

幻象引用於弱引用和軟引用均不同。它控制其指向的物件非常弱(tenuous),以至於它不能獲得這個物件。get()方法通常情況下返回的是null值。它唯一的作用就是跟蹤列隊在ReferenceQuene中的已經死去的物件。

幻象引用和弱引用的不同在於其入隊(enquene)進入ReferenceQuene的方式。當弱引用的物件成為若可到達時,弱引用即列隊進入ReferenceQuene。這個入隊發生在終結(finialize)和垃圾回收實際發生之前。理論上,通過不正規(unorthodox)的finilize()方法,成為垃圾的物件能重新復活(resurrected),但是弱引用仍然是死的。幻象引用只有當物件在物理上從記憶體中移出時,才會入隊。這就阻止我們重新恢復將死的物件。

W:終結(Finalization)指比拉圾回收更一般的概念,可以回收物件所佔有的任意資源,比如檔案描述符和圖形上下文等。

幻象引用由兩個好處:

A:它能確定某一個物件從記憶體中移除的時間,這也是唯一的方式。通常情況下,這不是非常有用。但是遲早(come in handy)會用到手動處理大圖片的情況:如果你確定一張圖片需要被垃圾回收,那麼在你嘗試載入下一張照片前,你應該等待這張照片被回收完成。這樣就使得令人恐懼的(dreaded)記憶體溢位不太可能發生。

B:虛幻引用能夠避終結(finilize)的基本問題。finilize()方法能夠通過給一個垃圾物件關聯一個強引用使之復活。問題是覆寫了finilize()方法的物件在成為垃圾之前,為了回收,垃圾回收期需要執行兩次單獨的迴圈。第一輪迴圈確定某個物件是垃圾,那麼它就符合終結finilize的條件。在finilize的過程中,這個物件可能被“復活”。在這個物件被實際移除之前,垃圾回收期不得不重新執行一遍。因為finilization並不是實時呼叫的,所以在終止進行的過程中,可能發生了gc的多次迴圈。在實際清理垃圾物件時,這導致了一些延時滯後。這將導致Heap中有大量的垃圾導致記憶體溢位。

幻象引用不可能發生以上的情況,當幻象引用入隊時,它實際上已經被移除了記憶體。幻象記憶體無法“復活”物件。這發現這個物件時虛幻可到達時,在第一輪迴圈中,它就被回收。

可以證明,finilize()方法從不在第一種情況下使用,但是虛幻引用提供了一種更安全和有效的使用和被排除掉的finilize方法的機制,使得垃圾回收更加簡單。但是因為有太多的東西需要實現,我通常不適用finilize。

W:Object類中相關內容如下:
這裡寫圖片描述
文件中相關內容如下:
文件
(四) 原文地址及其他翻譯