若是一個內存中的對象沒有任何引用的話,就說明這個對象已經再也不被使用了,從而能夠成爲被垃圾回收的候選。不過,因爲垃圾回收器的運行時間不肯定,因此可被垃圾回收的對象的實際被回收時間是不肯定的。對於一個對象來講,只要有引用的存在,它就會一直存在於內存中。若是這樣的對象愈來愈多,超出了JVM中的內存總數,JVM就會拋出OutOfMemory錯誤。java
雖然垃圾回收的具體運行是由JVM來控制的,可是開發人員仍然能夠在必定程度上與垃圾回收器進行交互,其目的在於更好的幫助垃圾回收器管理好應用的內存。數組
在Java中把對象的引用分爲 4 種級別,從而使程序能更加靈活地控制對象的生命週期。這 4 種級別由高到低依次爲:強引用、軟引用、弱引用和虛引用。緩存
JAVA對象引用類層次結構圖以下圖所示:網絡
圖14-1 JAVA對象引用類層次結構圖編輯器
1)強引用類型函數
在通常的Java程序中,見到最多的就是強引用(strong reference)。如咱們最多見的代碼this
[代碼]java代碼:
2 |
String str = 「Hello Wolrd」; |
3 |
Object obj = new Object(); |
4 |
Date date = new Date(), |
在上面的代碼中str、obj、date都是對象的強引用。對象的強引用能夠在程序中處處傳遞。不少狀況下,會同時有多個引用指向同一個對象。強引用的存在限制了對象在內存中的存活時間。假如對象A中包含了一個對象B的強引用,那麼通常狀況下,對象B的存活時間就不會短於對象A。若是對象A沒有顯式的把對象B的引用設爲null的話,就只有當對象A被垃圾回收以後,對象B纔再也不有引用指向它,纔可能得到被垃圾回收的機會。url
2)軟引用類型spa
軟引用(soft reference)在強度上弱於強引用,經過SoftReference類來表示。它的做用是告訴垃圾回收器,程序中的哪些對象並不那麼重要,當內存不足的時候是能夠被暫時回收的。當JVM中的內存不足的時候,垃圾回收器會釋放那些只被軟引用所指向的對象。若是所有釋放完這些對象以後,內存還不足,纔會拋出Out Of Memory錯誤。設計
軟引用很是適合於建立緩存。當系統內存不足的時候,緩存中的內容是能夠被釋放的。好比考慮一個圖像編輯器的程序,該程序會把圖像文件的所有內容都讀取到內存中,以方便進行處理,而用戶也能夠同時打開多個文件。當同時打開的文件過多的時候,就可能形成內存不足。若是使用軟引用來指向圖像文件內容的話,垃圾回收器就能夠在必要的時候回收掉這些內存。
從下面的Java代碼中能夠看到軟引用類型的使用方法。
[代碼]java代碼:
01 |
public class BitmapCache { |
02 |
private String url; //圖片URL |
03 |
private SoftReference<Bitmap> softRef; // //軟引用-只有當系統內存不足的時候纔去釋放 |
04 |
public BitmapCache (String url) { |
06 |
softRef = new SoftReference< Bitmap >( null ); |
08 |
private Bitmap loadRemoteBitmap() { |
09 |
final DefaultHttpClient client = new DefaultHttpClient(); |
10 |
final HttpGet getRequest = new HttpGet(url); |
11 |
HttpResponse response = client.execute(getRequest); |
12 |
final int statusCode = response.getStatusLine().getStatusCode(); |
13 |
final HttpEntity entity = response.getEntity(); |
14 |
InputStream inputStream = entity.getContent(); |
15 |
final ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); |
16 |
OutputStream outputStream = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE); |
17 |
copy(inputStream, outputStream); |
19 |
final byte [] data = dataStream.toByteArray(); |
20 |
final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0 , data.length); |
23 |
public Bitmap get Bitmap () { |
24 |
Bitmap bitmap = softRef.get(); |
25 |
if (bitmap == null ) { //系統內存不足的時,圖片已經被釋放須要從新加載網絡圖片 |
26 |
bitmap = loadRemoteBitmap (); |
27 |
softRef = new SoftReference< Bitmap >( bitmap); |
在使用上面程序的時候,因爲軟引用所指向的對象可能被回收掉,在經過get方法來獲取軟引用所實際指向的對象的時候,老是要檢查該對象是否還存活。
3)弱引用類型
弱引用(weak reference)在強度上弱於軟引用,經過WeakReference類來表示。它的做用是引用一個對象,可是並不阻止該對象被回收。若是使用一個強引用的話,只要該引用存在,那麼被引用的對象是不能被回收的,弱引用則沒有這個問題。在垃圾回收器運行的時候,若是一個對象的全部引用都是弱引用的話,該對象會被回收。弱引用的做用在於解決強引用所帶來的對象之間在存活時間上的耦合關係。
弱引用最多見的用處是在集合類中,尤爲在哈希表中。哈希表的接口容許使用任何Java對象做爲鍵(Key)。當一個鍵值對(Key-Value)被放入到哈希表中以後,哈希表對象自己就有了對這些鍵和值對象的引用。若是這種引用是強引用的話,那麼只要哈希表對象自己還存活,其中所包含的鍵和值對象是不會被回收的。若是某個存活時間很長的哈希表中包含的鍵值對不少,最終就有可能消耗掉JVM中所有的內存。對於這種狀況的解決辦法就是使用弱引用來引用這些對象,這樣哈希表中的鍵和值對象都能被垃圾回收器及時回收。在Java中可使用WeakHashMap類來知足這一常見需求。
4)虛引用類型
在介紹虛引用以前,先要了解一下Java提供的對象終止化機制(finalization)。
在Object類裏面有個finalize方法,其設計的初衷是在一個對象被真正回收以前,能夠用來執行一些清理的工做。由於Java並無提供相似C++的析構函數同樣的機制,只是簡單地經過 finalize方法來實現。可是問題在於垃圾回收器的運行時間是不固定的,因此這些清理工做的實際運行時間也是不能預知的。
使用虛引用(phantom reference)能夠解決這個問題。在建立虛引用PhantomReference的時候必需要指定一個引用隊列。當一個對象的finalize方法已經被調用了以後,這個對象的虛引用會被加入到隊列中。經過檢查該隊列裏面的內容就知道一個對象是否是已經準備要被回收了。
虛引用及其隊列的使用狀況並很少見,主要用來實現比較精細的內存使用控制,這對於移動設備來講是頗有意義的。程序能夠在肯定一個對象要被回收以後,再申請內存建立新的對象。經過這種方式可使得程序所消耗的內存維持在一個相對較低的數量。
好比下面的Java代碼給出了一個緩衝區的實現示例。
[代碼]java代碼:
02 |
public class PhantomBuffer { |
04 |
private byte [] data = new byte [ 0 ]; |
06 |
private ReferenceQueue< byte []> queue = new ReferenceQueue< byte []>(); |
08 |
private PhantomReference< byte []> ref = new PhantomReference< byte []>(data, queue); |
10 |
public byte [] get( int size) { |
14 |
throw new IllegalArgumentException( "Wrong buffer size" ); |
16 |
//檢查當前緩衝區是否能知足申請的緩衝區大小 |
17 |
if (data.length < size) { |
28 |
data = new byte [size]; |
30 |
ref = new PhantomReference< byte []>(data, queue); |
31 |
} catch (InterruptedException e) { |
在上面的代碼中,每次申請新的緩衝區的時候,都首先確保以前的緩衝區的字節數組已經被成功回收。引用隊列的remove方法會阻塞直到新的虛引用被加入到隊列中。須要注意的是,這種作法會致使垃圾回收器被運行的次數過多,可能會形成程序的吞吐量太低。
5)引用隊列
在有些狀況下,程序會須要在一個對象的可達到性發生變化的時候獲得通知。好比某個對象的強引用都已經不存在了,只剩下軟引用或是弱引用。可是還須要對引用自己作一些的處理。典型的情景是在哈希表中,引用對象是做爲WeakHashMap中的鍵對象的,當其引用的實際對象被垃圾回收以後,就須要把該鍵值對從哈希表中刪除。有了引用隊列(ReferenceQueue),就能夠方便的獲取到這些弱引用對象,將它們從表中刪除。在軟引用和弱引用對象被添加到隊列以前,其對實際對象的引用會被自動清空。經過引用隊列的poll/remove方法就能夠分別以非阻塞和阻塞的方式獲取隊列中的引用對象。