【轉】Java之WeakReference與SoftReference使用講解

Java 2 平臺引入了 java.lang.ref 包,其中包括的類可讓您引用對象,而不將它們留在內存中。這些類還提供了與垃圾收集器(garbage collector)之間有限的交互。java

      1.先「由強到弱」(只的是和垃圾回收器的關係)明確幾個基本概念:
      strong references是那種你一般創建的reference,這個reference就是強可及的。這個不會被垃圾回收器自動回收。例如:
      StringBuffer buffer = new StringBuffer();
      其中這個buffer就是強引用,之因此稱爲「強」是取決於它如何處理與Garbage Collector的關係的:它是不管如何都不會被回收的。夠強的。強引用在某些時候是有個問題的,下邊的一個哈希表實例就是很好的說明。並且還有一個問題就是在緩衝上,尤爲是諸如圖片等大的結構上。咱們在內存中開闢一塊區域放置圖片緩衝,那咱們就但願有個指針指向那塊區域。此時如果使用強引用則會強迫圖片留在內存,當你以爲不須要的時候你須要手動移除,不然就是內存泄漏。數組

      WeakReference則相似於無關緊要的東西。在垃圾回收器線程掃描它所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存,說白了就是一個沒那麼strong要求垃圾回收器將一個對象保留在內存中。不過,因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些只具備弱引用的對象。常說的Unreachable和弱引用指代的是一個意思。這可能仍是說不清楚,那麼我舉個例子:
      你有一個類叫作Widget,可是因爲某種緣由它不能經過繼承來添加一項功能。當咱們想從這個對象中取出一些信息的時候怎麼辦呢?假設咱們須要監視每一個 Widget的serial Number,可是這個Widget卻恰恰沒有這個屬性,並且還不可繼承...這時候咱們想到了用 HashMaps:serialNumberMap.put(widget, widgetSerialNumber);
       這不就截了嘛~表面上看起來是ok的,可是正是Widget這個Strong Reference產生了問題。當咱們設定某個Widget的SerialNumber不須要的時候,那麼要從這個映射表中除去這個映射對,不然咱們就有了內存泄漏或者是出錯(移除了有效的SerialNumber)。這個問題聽起來很耳熟,是的,在沒有垃圾管理機制的語言中這是個常見問題,在JAVA中咱們不用擔憂。由於咱們有WeakReference。咱們使用內置的WeakHashMap類,這個類和哈希表HashMap幾乎同樣,但就是在鍵 key的地方使用了WeakReference,若一個WeakHashMap key成爲了垃圾,那麼它對應的入口就會自動被移除。這就解決了上述問題~緩存

      SoftReference則也相似於無關緊要的東西。若是內存空間足夠,垃圾回收器就不會回收它,若是內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就能夠被程序使用。軟引用可用來實現內存敏感的高速緩存。安全

      弱引用與軟引用的區別在於:具備WeakReference的對象擁有更短暫的生命週期。或者說SoftReference比WeakReference對回收它所指的對象不敏感。一個WeakReference對象會在下一輪的垃圾回收中被清理,而SoftReference對象則會保存一段時間。SoftReferences並不會主動要求與 WeakReference有什麼不一樣,可是實際上SoftReference對象通常在內存充裕時通常不會被移除,這就是說對於建立緩衝區它們是不錯的選擇。它兼有了StrongReference和WeakReference的好處,既能停留在內存中,又能在內存不足是去處理,這一切都是自動的!spa

      PhantomReference爲"虛引用",顧名思義,就是形同虛設,與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收,也就是說其get方法任什麼時候間都會返回null。虛引用主要用來跟蹤對象被垃圾回收的活動。其必須和引用隊列(ReferenceQueue)聯合使用,這是與弱引用和軟引用最大的不一樣。     線程

      WeakReference是在垃圾回收活動以前將對象入隊的,理論上講這個對象還可使用finalize()方法使之重生,可是WeakReference仍然是死掉了。 PhantomReferences對象是在對象從內存中清除出去的時候才入隊的。也就是說當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在回收對象的內存以前,把這個虛引用加入到與之關聯的引用隊列中。程序能夠經過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。程序若是發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起必要的行動。它限制了finalize()方法的使用,更安全也更高效。指針

     2.咱們看看這個包給咱們提供了什麼類?
WeakReference 類
WeakReference weakref = new WeakReference(ref);
      這樣 weakref 就是 ref 指向對象的一個 weak reference。要引用這個 weak reference 指向的對象能夠用 get 方法。把對象的 weak reference 放入 Hashtable 或者緩存中,當沒有 strong reference 指向他們的時候,對象就能夠被垃圾收集器回收了。實際上,有一個 WeakHashMap 就是專門作這個事的。一旦WeakReference使用get方法返回null的時候,它指向的對象已經變成了垃圾,這個weakref對象也沒什麼用處了。這就須要有一些清理工做了。而ReferenceQueue類就是作這個的,要是你向ReferenceQueue類傳遞了一個 WeakReference的構造方法,那麼當引用所指的對象成爲垃圾時,這個引用的對象就會被自動插入到這個引用隊列中。你能夠在必定時間間隔內處理這個隊列。對象

SoftReference 類 
     可用來實現智能緩存(java.lang.ref.SoftReference is a relatively new class, used to implement smart caches.)繼承

     假定你有一個對象引用,指向一個大數組:生命週期

Object obj = new char[1000000];
     而且若是可能的話,你打算一直保存這個數組,可是若是內存極其短缺的話,你樂於釋放這個數組。你可使用一個
soft reference:
SoftReference ref = new SoftReference(obj);
      Obj是這個soft reference的引用。在之後你用如下的方式檢測這個引用:
if (ref.get() == null)// (referent has been cleared)
else// (referent has not been cleared)
       若是這個引用已經被清除了,那麼垃圾回收器會收回它所使用的空間,而且你緩存的對象也已經消失。須要注意的是,若是這個指示物還有對它的別的引用,那麼垃圾回收器將不會清除它。這個方案能夠被用來實現各類不一樣類型的緩存,這些緩存的特色是隻要有可能對象就會被一直保存下來,可是若是內存緊張對象就被清除掉。
      注意:軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。

e.g.

Java代碼1.import java.lang.ref.*;  2. 3.public class References {  4.  public static void main(String[] args) {  5.    Object weakObj, phantomObj;  6.    Reference ref;  7.    WeakReference weakRef;  8.    PhantomReference phantomRef;  9.    ReferenceQueue weakQueue, phantomQueue;  10. 11.    weakObj    = new String("Weak Reference");  12.    phantomObj = new String("Phantom Reference");  13.    weakQueue    = new ReferenceQueue();  14.    phantomQueue = new ReferenceQueue();  15.    weakRef    = new WeakReference(weakObj, weakQueue);  16.    phantomRef = new PhantomReference(phantomObj, phantomQueue);  17. 18.    // Print referents to prove they exist.  Phantom referents  19.    // are inaccessible so we should see a null value.  20.    System.out.println("Weak Reference: " + weakRef.get());  21.    System.out.println("Phantom Reference: " + phantomRef.get());  22. 23.    // Clear all strong references  24.    weakObj    = null;  25.    phantomObj = null;  26. 27.    // Invoke garbage collector in hopes that references  28.    // will be queued  29.    System.gc();  30. 31.    // See if the garbage collector has queued the references  32.    System.out.println("Weak Queued: " + weakRef.isEnqueued());  33.    // Try to finalize the phantom references if not already  34.    if(!phantomRef.isEnqueued()) {  35.      System.out.println("Requestion finalization.");  36.      System.runFinalization();  37.    }  38.    System.out.println("Phantom Queued: " + phantomRef.isEnqueued());  39. 40.    // Wait until the weak reference is on the queue and remove it  41.    try {  42.      ref = weakQueue.remove();  43.      // The referent should be null  44.      System.out.println("Weak Reference: " + ref.get());  45.      // Wait until the phantom reference is on the queue and remove it  46.      ref = phantomQueue.remove();  47.      System.out.println("Phantom Reference: " + ref.get());  48.      // We have to clear the phantom referent even though  49.      // get() returns null  50.      ref.clear();  51.    } catch(InterruptedException e) {  52.      e.printStackTrace();  53.      return;  54.    }  55.  }  56.}

相關文章
相關標籤/搜索