1. 程式人生 > >java 軟引用、弱引用、強引用、虛引用的解析

java 軟引用、弱引用、強引用、虛引用的解析

寫了那麼多篇文章第一次使用MarkDown編輯器。。。

  • 軟引用與強引用、弱引用、虛引用的對比

軟引用、強引用、弱引用、虛引用

  • 強引用
  • 弱引用
  • 虛引用
  • 軟引用

強引用

強引用也就是我們一般使用的引用,如若一個物件有強引用,那麼即使記憶體不足的情況出現,強引用物件也不會被輕易的回收

String s = new String();

建立了一個String物件,並用一個變數s儲存對這個物件的引用。這就是個強引用。
變數持有的是這個物件的引用。通常,引用是一個物件的儲存地址(但不同於c++、c,這個引用不能轉換成整數)。而強引用其實就是正在使用的物件

虛引用

虛引用其實沒什麼用,和弱引用一樣不會介入引用物件的生命週期。它的get方法總是返回null,所以你得不到它引用的物件。它的唯一作用就是跟蹤物件合適被回收,但是必須要和引用佇列 (ReferenceQueue)聯合使用。當垃圾回收機制準備回收一個物件的時候,如果發現這個物件還存在一個虛引用,就會在這個物件被回收之前將這個虛引用加入到引用佇列中,如果在引用佇列中發現虛引用,那麼就說明與之關聯的物件將要被回收了。

弱引用

如果一個物件持有的是弱引用,垃圾回收機制是會去回收這個物件的,但是垃圾回收是優先順序很低的執行緒,所以不一定很快能被回收。這個會在什麼情況應用呢?看過android官網圖片載入demo的同學們應該就比較能理解,我這裡就直接拿過來用,作為弱引用的講解例子。

class BitmapWorkerTask extends AsyncTask {
    private final WeakReference imageViewReference;
    private int data = 0;

    public BitmapWorkerTask(ImageView imageView) {
        // Use a WeakReference to ensure the ImageView can be garbage collected
        imageViewReference = new WeakReference(imageView);
    }

    // Decode image in background.
@Override protected Bitmap doInBackground(Integer... params) { data = params[0]; return decodeSampledBitmapFromResource(getResources(), data, 100, 100)); } // Once complete, see if ImageView is still around and set bitmap. @Override protected void onPostExecute(Bitmap bitmap) { if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } } }

這裡就有一個imageview的弱引用,AsyncTask 這個大家都應該不陌生就不再介紹,這裡在這個非同步載入執行緒中載入圖片,然後顯示到imageview上面。而imageview是在ui執行緒(也就是主執行緒),我們不能保證使用者什麼時候會結束這個主執行緒,而此時已經啟動了這個圖片非同步載入執行緒,我們不知道它什麼時候會載入完,如果執行緒擁有的是imageview的強引用的話,這個imageview在使用者退出的時候就不能被回收,也就會造成記憶體洩露的情況,在使用handler的時候也經常會遇到這個問題,就是你在activity中啟動一個執行緒,使用handler處理資訊,如果activity退出,而執行緒是不會退出的,還會持有handler物件,也就造成了handler不能被回收,記憶體洩露就出現了。而弱引用就解決了這個問題,像上面的程式碼,ui執行緒結束imageview此時要回收,因為圖片載入執行緒中持有的是imageview的弱引用,他不會影響他所弱引用物件的回收,也就不會造成記憶體洩露,而此時如果弱引用的物件已經被回收,imageViewReference.get();得到的就是空

軟引用

軟引用在記憶體優化、速度優化中是經常會被用到的,軟引用可用來實現記憶體敏感的快取記憶體。如果一個物件有一個軟引用,那麼只要記憶體空間足夠,他就不會被回收,我們就可以使用這些物件,相當於回收利用吧。我們知道開闢記憶體的消耗還是比較大的(不管是事件還是空間上),所以複用就可以做到減少記憶體的消耗,速度優化等。可以和引用佇列聯合使用。
這裡要再次說下android官網的圖片載入demo,還是建議大家去看看。

這裡又要用到這裡面的例子程式來講軟引用,但是他的demo中沒有結合引用佇列使用,下面就結合demo介紹:
首先來說說他的應用場景:非同步載入大量的圖片,大量圖片的話就必定會用到記憶體快取以提高速度。使用的是LruCache來快取圖片,也就是結合了最近最久未使用演算法的快取,這個快取要有大小限制的(要是把全部記憶體都佔了那肯定不行),也就是會有圖片從這個快取中移除,這個時候就用到了軟引用,將那些所有被移出快取的圖片(bitmap物件)繫結軟引用,加入到一個容器中儲存,然後等到需要新的bitmap物件的時候就從這個容器中迴圈拿可以使用的進行復用(建立新的bitmap記憶體很耗時,也很費記憶體,複用大大提高效率)。
下面就是被移除的物件新增軟引用的程式碼

mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {

    // Notify the removed entry that is no longer being cached.
    @Override
    protected void entryRemoved(boolean evicted, String key,
            BitmapDrawable oldValue, BitmapDrawable newValue) {
        if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
            // The removed entry is a recycling drawable, so notify it
            // that it has been removed from the memory cache.
            ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
        } else {
            // The removed entry is a standard BitmapDrawable.
            if (Utils.hasHoneycomb()) {
                // We're running on Honeycomb or later, so add the bitmap
                // to a SoftReference set for possible use with inBitmap later.
                mReusableBitmaps.add
                        (new SoftReference<Bitmap>(oldValue.getBitmap()));
            }
        }
    }
....

迴圈找可以複用的bitmap

protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
        Bitmap bitmap = null;

    if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
        synchronized (mReusableBitmaps) {
            final Iterator<SoftReference<Bitmap>> iterator
                    = mReusableBitmaps.iterator();
            Bitmap item;

            while (iterator.hasNext()) {
                item = iterator.next().get();

                if (null != item && item.isMutable()) {
                    // Check to see it the item can be used for inBitmap.
                    if (canUseForInBitmap(item, options)) {
                        bitmap = item;

                        // Remove from reusable set so it can't be used again.
                        iterator.remove();
                        break;
                    }
                } else {
                    // Remove from the set if the reference has been cleared.
                    iterator.remove();
                }
            }
        }
    }
    return bitmap;
}

上面我們說了要結合引用佇列使用,但是上面的而梨子中其實沒有用到。先說說引用佇列的用處吧:

String s = new String();
ReferenceQueue queue = new  ReferenceQueue();
SoftReference  ref=new  SoftReference(s, queue);

如上述程式碼 ,一開始s是強引用,然後建立了一個引用佇列,以及結合了引用佇列的軟引用,此時如果s=null;(上面也講到了,垃圾回收是一個優先順序很低的執行緒,所以不會那麼快回收),但這裡假設s被回收了,那麼ref就會加入到queue中,此時ref通過get方法得到的結果已經是null了,我們可以利用這個來清除沒有用的軟引用,如果像上面這樣是單個的軟引用物件的話就可以不用,但是很多時候我們會有一堆軟引用然後都放在容器中,這個時候就需要去清理其中沒有用的軟引用,因為如果那個存軟引用的容器存了一堆沒用的,也是很大的消耗。
引用佇列的使用以及為什麼要使用也說了,為什麼上面android官網的載入圖片的demo中沒有用到引用佇列大家結合一下程式碼看看就知道了,引用佇列就一個用處:清理沒用的軟引用,而上面的demo中我們可以看到他迴圈讀取其中的軟引用,判斷是否為空(也就是沒用了的軟引用)等條件,不符合就直接移除了,也就做了清理沒用的軟引用的工作。

講完了。。。最後要說,軟引用、弱引用真的是比較好用的,一定要學會靈活使用。