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.}