天天看點

java之弱引用、軟引用、虛引用

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

      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的好處,既能停留在記憶體中,又能在記憶體不足是去處理,這一切都是自動的!

      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虛拟機就會把這個軟引用加入到與之關聯的引用隊列中。

可以通過一個例子來看看他們在垃圾收集時的表現:

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

     
  public class References {  
	  
    public static void main(String[] args) {  
      String weakObj, phantomObj, softObj;  
      Reference<? extends String> ref;  
      WeakReference<String> weakRef;  
      PhantomReference<String> phantomRef;  
      SoftReference<String> softRef;
      ReferenceQueue<String> weakQueue, phantomQueue, softQueue;  
    
       weakObj    = new String("Weak Reference");  
       phantomObj = new String("Phantom Reference");
       softObj = new String("Soft Reference");
       weakQueue    = new ReferenceQueue<String>();  
       phantomQueue = new ReferenceQueue<String>();  
       softQueue = new ReferenceQueue<String>();
       weakRef    = new WeakReference<String>(weakObj, weakQueue);  
       phantomRef = new PhantomReference<String>(phantomObj, phantomQueue);  
       softRef = new SoftReference<String>(softObj, softQueue);
    
       // Print referents to prove they exist   Phantom referents  
       // are inaccessible so we should see a null value   
       System.out.println("Weak Reference: " + weakRef.get());  
       System.out.println("Phantom Reference: " + phantomRef.get());  
       System.out.println("Soft Reference: " + softRef.get());
    
       // Clear all strong references  
       weakObj    = null;  
       phantomObj = null;  
       softObj = null;
    
       // Invoke garbage collector in hopes that references  
       // will be queued  
       System.gc();  
    
       // See if the garbage collector has queued the references  
       System.out.println("Weak Queued: " + weakRef.isEnqueued());  
       // Try to finalize the phantom references if not already  
       if(!phantomRef.isEnqueued()) {  
         System.out.println("Requestion finalization ");  
         System.runFinalization();  
       }  
       System.out.println("Phantom Queued: " + phantomRef.isEnqueued());  
       System.out.println("Soft Queued: " + softRef.isEnqueued()); 
    
       // Wait until the weak reference is on the queue and remove it  
       try {  
         ref = weakQueue.remove();  
         // The referent should be null  
         System.out.println("Weak Reference: " + ref.get());  
         // Wait until the phantom reference is on the queue and remove it  
         ref = phantomQueue.remove();  
         System.out.println("Phantom Reference: " + ref.get());  
         ref = softQueue.remove();  
         System.out.println("Soft Reference: " + ref.get());  
         // We have to clear the phantom referent even though  
         // get() returns null  
         ref.clear();  
       } catch(InterruptedException e) {  
         e.printStackTrace();  
         return;  
       }  
     }  
   }
           

 輸出的結果是這樣的:

Weak Reference: Weak Reference

Phantom Reference: null

Soft Reference: Soft Reference

Weak Queued: true

Phantom Queued: true

Soft Queued: false

Weak Reference: null

Phantom Reference: null

如果記憶體空間足夠,垃圾回收器就不會回收軟引用,如果記憶體空間不足了,才會回收這引起對象的記憶體。一般程式會block在這一句:

ref = softQueue.remove();
           

本文在http://blog.sina.com.cn/s/blog_8417aea80100skwx.html的基礎上修改。