Java四種引用使用詳解

爲什麼要定義四種引用?

一、讓程序員可以通過代碼的方式決定某些對象的生命週期;

二、有利於JVM進行垃圾回收。

強引用

創建一個對象並把對象賦給一個引用變量

強引用有引用變量指向時永遠不會被垃圾回收,JVM寧願拋出OutOfMemory錯誤也不會回收這種對象。永遠都不會被回收

public class Test {
    public static void main(String[] args) {
        new Test().fun1();
    }

    public void fun1() {
        Object object = new Object();
        Object[] objArr = new Object[1000];
    }
}

在運行Object[] objArr = new Object[1000];這一句時,就算JVM拋出OOM異常,也不會被回收。

不過在運行完fun1函數後,因爲object和objArr
兩個引用變量不存在了,他們指向的對象都會在某個時候被JVM回收

PS:

String str ="hello";就算str不存在了,”hello”不會被回收,因爲”hello”位於常量區,而JVM垃圾回收的是堆區內存。

軟引用(SoftReference):

如果一個對象具有軟引用,內存空間足夠,垃圾回收器就不會回收它。

如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。

軟引用可用來實現內存敏感的高速緩存,比如網頁緩存、圖片緩存等。使用軟引用能防止內存泄露,增強程序的健壯性。

SoftReference的特點是它的一個實例保存對一個Java對象的軟引用, 該軟引用的存在不妨礙垃圾收集線程對該Java對象的回收。

也就是說,一旦SoftReference保存了對一個Java對象的軟引用後,在垃圾線程對 這個Java對象回收前,SoftReference類所提供的get()方法返回Java對象的強引用。 另外,一旦垃圾線程回收該Java對象之 後,get()方法將返回null。

MyObject aRef = new  MyObject();  
SoftReference aSoftRef=new SoftReference(aRef); 
aRef = null;
MyObject anotherRef=(MyObject)aSoftRef.get(); 

代碼的前兩句,因爲有強引用的存在,所以是強可及對象,不會被回收,在執行aRef=null後,就只剩軟引用了,軟可及對象會在內存不足的時候被回收。軟可及對象的清理是由垃圾收集線程根據其特定算法按照內存需求決定的。

作爲一個Java對象,SoftReference對象除了具有保存軟引用的特殊性之外,也具有Java對象的一般性。所以,當軟可及對象被回收之後,雖然這個SoftReference對象的get()方法返回null,但這個SoftReference對象已經不再具有存在的價值,需要一個適當的清除機制,避免大量SoftReference對象帶來的內存泄漏。在java.lang.ref包裏還提供了ReferenceQueue。如果在創建SoftReference對象的時候,使用了一個ReferenceQueue對象作爲參數提供給SoftReference的構造方法,如:

ReferenceQueue queue = new  ReferenceQueue();  
SoftReference  aSoftRef = new  SoftReference(aMyObject, queue);  

當這個SoftReference所軟引用的Object被垃圾收集器回收的同時,aSoftRef所強引用的SoftReference對象被列入ReferenceQueue。也就是說,ReferenceQueue中保存的對象是Reference對象,而且是已經失去了它所軟引用的對象的Reference對象。另外從ReferenceQueue這個名字也可以看出,它是一個隊列,當我們調用它的poll()方法的時候,如果這個隊列中不是空隊列,那麼將返回隊列前面的那個Reference對象。

在任何時候,我們都可以調用ReferenceQueue的poll()方法來檢查是否有它所關心的非強可及對象被回收。如果隊列爲空,將返回一個null,否則該方法返回隊列中前面的一個Reference對象。利用這個方法,我們可以檢查哪個SoftReference所軟引用的對象已經被回收。於是我們可以把這些失去所軟引用的對象的SoftReference對象清除掉。常用的方式爲:

SoftReference ref = null;  
while ((ref = (EmployeeRef) q.poll()) != null) {  
    // 清除ref  
}  

弱引用(WeakReference)

弱引用也是用來描述非必需對象的,當JVM進行垃圾回收時,無論內存是否充足,都會回收被弱引用關聯的對象。在java中,用java.lang.ref.WeakReference類來表示。下面是使用示例:

public class Test {
    public static void main(String[] args) {
        WeakReference<String>reference=new WeakReference<String>(new String("zhangsan"));
        System.out.println(reference.get());
        System.gc();//通知JVM回收資源
        System.out.println(reference.get());
    }

}

結果:

zhangsan

null

只要JVM進行垃圾回收,被弱引用關聯的對象必定會被回收掉。不過要注意的是,這裏所說的被弱引用關聯的對象是指只有弱引用與之關聯,如果存在強引用同時與之關聯,則進行垃圾回收時也不會回收該對象(軟引用也是如此)

弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,(與軟引用一樣)如果弱引用所引用的對象被JVM回收,這個弱引用就會被加入到與之關聯的引用隊列中。

虛引用(PhantomReference)

虛引用和前面的軟引用、弱引用不同,它並不影響對象的生命週期。在java中用java.lang.ref.PhantomReference類表示。如果一個對象與虛引用關聯,則跟沒有引用與之關聯一樣,在任何時候都可能被垃圾回收器回收。
虛引用一般用來檢測對象是否被回收

  要注意的是,虛引用必須和引用隊列關聯使用,當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會把這個虛引用加入到與之 關聯的引用隊列中。程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。如果程序發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動。
  

public class Test {
    public static void main(String[] args) {
        ReferenceQueue<String> queue = new ReferenceQueue<String>();
        PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
        System.out.println(pr.get());
    }

}

利用軟引用弱引用解決OOM問題

爲了防止內存溢出,在處理一些佔用內存大而且聲明週期較長的對象時候,可以儘量應用軟引用和弱引用技術。

在應用中中可能需要使用到大量的圖片,大量的圖片可能會導致OOM異常,這時可以使用軟引用來避免這個問題。

首先定義一個HashMap,保存軟引用對象。

private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();

再來定義一個方法,保存Bitmap的軟引用到HashMap。

 public void addBitmapToCache(String path) {
        // 強引用的Bitmap對象
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        // 軟引用的Bitmap對象
        SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);
        // 添加該對象到Map中使其緩存
        imageCache.put(path, softBitmap);
    }

獲取的時候,可以通過SoftReference的get()方法得到Bitmap對象。

 public Bitmap getBitmapByPath(String path) {
        // 從緩存中取軟引用的Bitmap對象
        SoftReference<Bitmap> softBitmap = imageCache.get(path);
        // 判斷是否存在軟引用
        if (softBitmap == null) {
            return null;
        }
        // 取出Bitmap對象,如果由於內存不足Bitmap被回收,將取得空
        Bitmap bitmap = softBitmap.get();
        return bitmap;
    }

如果只是想避免OutOfMemory異常的發生,則可以使用軟引用。如果對於應用的性能更在意,想盡快回收一些佔用內存比較大的對象,則可以使用弱引用。
還有就是可以根據對象是否經常使用來判斷。如果該對象可能會經常使用的,就儘量用軟引用。如果該對象不被使用的可能性更大些,就可以用弱引用。
另外,和弱引用功能類似的是WeakHashMap。WeakHashMap對於一個給定的鍵,其映射的存在並不阻止垃圾回收器對該鍵的回收,回收以後,其條目從映射中有效地移除。WeakHashMap使用ReferenceQueue實現的這種機制。

refer:
Android開發優化之——使用軟引用和弱引用
http://blog.csdn.net/arui319/article/details/8489451

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章