有效避免OOM--合理使用軟引用和弱引用

robert_chao發表於2016-06-05

在JDK 1.2以前的版本中,若一個物件不被任何變數引用,那麼程式就無法再使用這個物件。也就是說,只有物件處於可觸及(reachable)狀態,程式才能使用它。從JDK 1.2版本開始,把物件的引用分為4種級別,從而使程式能更加靈活地控制物件的生命週期。這4種級別由高到低依次為:強引用、軟引用、弱引用和虛引用

StrongReference

強引用是使用最普遍的引用。如果一個物件具有強引用,那垃圾回收器絕不會回收它。當記憶體空間不足,Java虛擬機器寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足的問題。,這是java預設的引用型別,如果不特意使用java.lang.ref下的類,那麼程式中的所有引用都是強引用。

SoftReference

軟引用要用java.lang.ref.SoftReference來實現,如果一個物件只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。軟引用可用來實現記憶體敏感的快取記憶體。
軟引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果軟引用所引用的物件被垃圾回收器回收,Java虛擬機器就會把這個軟引用加入到與之關聯的引用佇列中。

public class SoftTest{    
    public static void main(String[] args) {    
      Object ref = new Object();//ref是Object物件的強引用    
      //將一個軟引用指向物件,此時Object物件有兩個引用    
      SoftReference<Object> sf = new SoftReference<Object>(ref);       
      ref = null;//去除物件的強引用    
      System.gc();//gc只有在記憶體不足是才會回收軟引用物件    
    }    
} 

WeakReference

除了通過java.lang.ref.WeakReference來使用弱引用,WeakHashMap同樣也利用了弱引用。 和軟引用不同的是,弱引用一定會被gc回收,不管記憶體是否不足。
弱引用與軟引用的區別在於:只具有弱引用的物件擁有更短暫的生命週期。在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。不過,由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快發現那些只具有弱引用的物件。
弱引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果弱引用所引用的物件被垃圾回收,Java虛擬機器就會把這個弱引用加入到與之關聯的引用佇列中。

public class WeakTest{    
    public static void main(String[] args) {    
        Object ref = new Object();//ref是Object物件的強引用       
        //將一個弱引用指向物件,此時Object物件有兩個引用    
       WeakReference<Object> wf = new WeakReference<Object>(ref);    
        ref = null;//去除物件的強引用    
       System.gc();//gc對弱引用物件進行回收    
    }    
}    

PhantomReference

幽靈引用,也叫虛引用。java.lang.ref.PhantomReference類中只有一個方法get(),而且幾乎沒有實現,只是返回null。而且這個類只有一個構造器(軟引用和弱引用均有兩個構造器)。
“虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定物件的生命週期。如果一個物件僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。
虛引用主要用來跟蹤物件被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個物件時,如果發現它還有虛引用,就會在回收物件的記憶體之前,把這個虛引用加入到與之 關聯的引用佇列中。
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);
程式可以通過判斷引用佇列中是否已經加入了虛引用,來了解被引用的物件是否將要被垃圾回收。如果程式發現某個虛引用已經被加入到引用佇列,那麼就可以在所引用的物件的記憶體被回收之前採取必要的行動。
public class PhantomTest{    
     public static void main(String[] args) {    
        Object ref = new Object();//ref是Object物件的強引用      
        //將一個幽靈引用指向物件,PhantomReference必須與ReferenceQueue一同使用    
        PhantomReference<Object> pf = new PhantomReference<Object>(ref, new ReferenceQueue<Object>());  
    System.out.println(pf.get());    
     }    
}  

android中的應用

在http://blog.csdn.net/robertcpp/article/details/51563318這篇部落格中講過AsyncQueryHandler的使用
AsyncQueryHandler的原始碼中為了防止記憶體洩露,使用了WeakReference來儲存mResolver

public abstract class AsyncQueryHandler extends Handler {  
    private static final String TAG = "AsyncQuery";  
    private static final boolean localLOGV = false;  
  
    private static final int EVENT_ARG_QUERY = 1;  
    private static final int EVENT_ARG_INSERT = 2;  
    private static final int EVENT_ARG_UPDATE = 3;  
    private static final int EVENT_ARG_DELETE = 4;  
  
    /* package */ final WeakReference<ContentResolver> mResolver;  
ImageLoader使用WeakReference做快取處理
public abstract class BaseMemoryCache implements MemoryCache {  
  
    /** Stores not strong references to objects */  
    private final Map<String, Reference<Bitmap>> softMap = Collections.synchronizedMap(new HashMap<String, Reference<Bitmap>>());  
    ......  
  
    @Override  
    protected Reference<Bitmap> createReference(Bitmap value) {  
        return new WeakReference<Bitmap>(value);  
}  
基本可以這麼說
weakReference一般用來防止記憶體洩漏,要保證記憶體被VM回收 
softReference的話,多用作來實現cache機制.
直接使用handler經常看到這樣的提示
 The following Handler class should be static or leaks might occur
Handler設定為static,不能訪問外部非static變數
我們可以這樣寫來解決這個問題

private static class MyHandler extends Handler {  
    private final WeakReference<SampleActivity> mActivity;  
  
    public MyHandler(SampleActivity activity) {  
      mActivity = new WeakReference<SampleActivity>(activity);  
    }  
  
    @Override  
    public void handleMessage(Message msg) {  
      SampleActivity activity = mActivity.get();  
      if (activity != null) {  
        // ...  
      }  
    }  
 }  
不會記憶體洩露,Handler中也能訪問到activity中的非靜態變數


歡迎掃描二維碼,關注公眾號




相關文章