Java/Android中的強引用、軟引用、弱引用、虛引用

very_on發表於2018-06-24

引用分為四個,從高到低的級別以此為強引用-軟引用-弱引用-虛引用.

  • 引用型別

    類別回收機制用途生存時間
    強引用從不回收物件狀態JVM停止執行時
    軟引用記憶體不足時進行回收快取記憶體不足
    弱引用物件不被引用時回收快取GC執行後
    虛引用物件被回收時管理控制精確記憶體穩定性unknown

強引用

Qiang qiang=new Qiang();
Niu niu=new Niu(qiang)

強引用例子,niu持有qiang的引用,當qiang=null的時候,並不能回收,而niu需要qiang,導致記憶體洩漏,典型的引用洩漏.

特點:

  • 即便OOM也不會發生回收.
  • 強引用在引用物件null時會導致記憶體洩漏
  • 強引用可以直接訪問目標物件

軟引用

    A a = new  A();
    SoftReference aSoftRef=new SoftReference(a);

    A a1=(A)a.get();

一個物件只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體。
只要垃圾回收器沒有回收它,該物件就可以被程式使用。

軟引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果軟引用所引用的物件被垃圾回收,Java虛擬機器就會把這個軟引用加入到與之關聯的引用佇列中。

    ReferenceQueue queue = new  ReferenceQueue();
    SoftReference  softReference=new  SoftReference(軟引用物件, queue);

Android中的示例

  • 案例1:

在平常Android開發中,有很多的圖片要顯示,如果是網路的則通過網路解析獲取,如果
每次都從網路解析影響體驗,那麼我們會將其儲存到本地,如果每次從本地獲取相對於我們將獲取後的圖片快取下來直接從內
存中獲取效率更低.但是因為圖片的數量多,消耗記憶體過大,快取圖片的過程需要大量的記憶體,記憶體不夠則會OOM,這時便可以採用軟引用的技術來解決問題.

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


    /**
     *
     * @param path
     */
    public  void addBitmap(String path){
        // 強引用的Bitmap物件
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        // 軟引用的Bitmap物件
        SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);
        // 新增softBitmap到Map中使其快取
        softReferenceMap.put(path, softBitmap);
    }

    /**
     *
     * @param path
     * @return
     */
    public Bitmap getBitmap(String path) {
        // 從快取中取軟引用的Bitmap物件
        SoftReference<Bitmap> softBitmap = softReferenceMap.get(path);
        // 判斷是否存在軟引用
        if (softBitmap == null) {
            return null;
        }
        // 取出Bitmap物件,如果由於記憶體不足Bitmap被回收,將取得空
        Bitmap bitmap = softBitmap.get();
        return bitmap;
    }

在softBitmap.get()中獲取Bitmap的例項的強引用,在記憶體充足的情況下不會回收軟引用物件,可以取出bitmap

記憶體不足時,softBitmap.get()不在返回bitamp直接返回null,軟引用被回收了

因此在獲取Bitmap的物件之前要判斷softBitmap == null是否為空,負責將會出現空指標異常.

  • 案例2

設計給出了一張1080的全屏圖片,這張圖片假設500K,可以想象它的記憶體消耗,通用使用軟引用來解決

public class SoftReferenceActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ImageView imageView=new ImageView(this);
        BitmapFactory.Options options=new BitmapFactory.Options();
        options.inPreferredConfig= Bitmap.Config.ARGB_4444;
        options.inPurgeable= true;
        options.inInputShareable= true;
        options.outWidth= 720;
        options.outHeight= 1280;
        Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round,options);
        Drawable drawable=new BitmapDrawable(getResources(),bitmap);
        SoftReference<Drawable> drawableSoftReference =
                new SoftReference<Drawable>(drawable);
        if(drawableSoftReference != null) {
            //記憶體不足將不顯示
            imageView.setBackground(drawableSoftReference.get());
        }

    }
}

因此在特定的場景想要避免OOM的發生就儘量使用軟引用吧.

但是在Android中最好選擇Least Recently Used(LRU),在它內部維護一個特定大小記憶體,在記憶體不足時會根據一系列的策略演算法來進行處理移除掉當前一些快取以便獲取新的記憶體空間用來快取資料.

弱引用

 WeakReference<MainActivity> weakReference = new WeakReference<MainActivity>(new MainActivity()) ;

如果一個物件只具有弱引用,那麼在垃圾回收器執行緒掃描的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體.

不過,由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快發現那些只具有弱引用的物件。

弱引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果弱引用所引用的物件被垃圾回收,Java虛擬機器就會把這個弱引用加入到與之關聯的引用佇列中。

Android中的示例

在最常見的Handler持有一個Activity的引用,Handler作為一個耗時的非同步執行緒處理,如果在處理過程中把Activity關閉了,因為Handler還持有Activity的引用,而一個非同步執行緒持有Handler引用,那麼就將導致記憶體洩漏

解決方案:

  • 在Activity關閉的地方將執行緒停止以及把Handler的訊息佇列的所有訊息物件移除
  • Handler改為靜態類

這裡我們使用將Handler改為靜態類,但是因為靜態類無法持有外部引用,就需要建立一個隊Activity的弱引用了

public class WeakReferenceActivity extends AppCompatActivity {

    private WeakReference<WeakReferenceActivity> reference;
    private MyHandler myHandler = new MyHandler(this);

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        myHandler.sendEmptyMessage(1);
    }


    public static class MyHandler extends Handler {
        private WeakReference<WeakReferenceActivity> reference;

        public MyHandler(WeakReferenceActivity activity) {
            reference = new WeakReference<WeakReferenceActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    WeakReferenceActivity weakReferenceActivity = (WeakReferenceActivity) reference.get();
                    if (weakReferenceActivity != null) {
                        System.out.print("WeakReferenceActivity");
                    }
                    break;

                default:
                    break;
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        myHandler.removeCallbacksAndMessages(null);
    }
}

虛引用

在物件銷燬時會被回收.

在Java中GC的執行時間是不確定的,在Java裡有一個finalize方法,在垃圾回收器準備釋放記憶體的時候,會先呼叫finalize().但因為記憶體還沒有好耗盡,擬機不能保證在適當的時機呼叫finalize,因此垃圾回收期與finalize是不可靠的方法.

這時可以採用虛引用.比如一個固定的記憶體,在明確知道一個bitmap回收之後會釋放一部分記憶體,新釋放開闢的記憶體就可以讓其他bitmap來使用,以此迴圈來達到記憶體的穩定性控制.

總結

在Android中比較常用便是 弱引用軟引用了。

對於一些OOM等常規處理使用軟引用便可很好的解決,可以實現快取記憶體.

對於偶爾使用的物件,並且隨時獲取到便使用弱引用來標記

軟引用與弱引用的區別:只含有弱引用的物件的生命週期更短。在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。不過,由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快發現那些只具有弱引用的物件。

虛引用只要用於記憶體的精準控制,如Android中的ViewPager圖片的檢視等等



作者:Ch3r1sh
連結:https://www.jianshu.com/p/017009abf0cf
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。

相關文章