工作中遇到的Android記憶體最佳化問題(1)

來源:互聯網
上載者:User

標籤:

最近工作中,遇到了幾個記憶體最佳化的問題,1.應用退出後,此應用進程保持了不少記憶體得不到釋放,用工具強制gc也無法釋放。2.應用進入某些頁面瞬間請求分配記憶體過大。此兩個問題對於有經驗的開發人員很容易猜測一個是記憶體泄露,一個是圖片之類的資源問題。下面來寫一個例子分析一下這兩個問題

 第一個例子是Volley載入圖片的app,當此app退出時緩衝釋放問題


Application類

package demo.memory.com.memorydemo;import android.app.Application;import com.android.volley.RequestQueue;import com.android.volley.toolbox.Volley;public class MyApplication extends Application{    RequestQueue mRequestQueue;    private static MyApplication mInstance;    public static MyApplication getInstance(){        return mInstance;    }    @Override    public void onCreate() {        super.onCreate();        mInstance = this;    }    public RequestQueue getRequestQueue() {        if (mRequestQueue == null) {            mRequestQueue = Volley.newRequestQueue(this);        }        return mRequestQueue;    }}


主Activity簡單的跳轉功能

package demo.memory.com.memorydemo;import android.content.Intent;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void jump(View view){        Intent intent = new Intent(this,ShowImageActivity.class);        startActivity(intent);    }}

載入圖片的Activity


package demo.memory.com.memorydemo;import android.app.Activity;import android.os.Bundle;import android.widget.ImageView;import com.android.volley.toolbox.ImageLoader;public class ShowImageActivity extends Activity{    private final static String IMAGE1_URL = "http://o6lxzg30h.bkt.clouddn.com/7375cd24ee25d29c81dff09a7375fff1.jpg";    private final static String IMAGE2_URL = "http://o6lxzg30h.bkt.clouddn.com/223412884cpmc7m4j1gof1.jpg";    ImageLoader mImageLoader;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.show_image);        mImageLoader = new ImageLoader(MyApplication.getInstance().getRequestQueue(), MyImageCache.getInstance());        ImageView image1Iv = (ImageView)findViewById(R.id.image1_iv);        ImageView image2Iv = (ImageView)findViewById(R.id.image2_iv);        mImageLoader.get(IMAGE1_URL,new MyImageListener(image1Iv));        mImageLoader.get(IMAGE2_URL,new MyImageListener(image2Iv));    }}


其他工具類

package demo.memory.com.memorydemo;import android.graphics.Bitmap;import android.util.LruCache;import com.android.volley.toolbox.ImageLoader;public class MyImageCache implements ImageLoader.ImageCache {    private LruCache<String, Bitmap> mMemoryCache;    private static MyImageCache mImageCache;    private MyImageCache() {        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);        int cacheSize = maxMemory / 8;        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {            @Override            protected int sizeOf(String key, Bitmap bitmap) {                return bitmap.getByteCount() / 1024;            }        };    }    public static MyImageCache getInstance() {        if (mImageCache == null)            mImageCache = new MyImageCache();        return mImageCache;    }    @Override    public void putBitmap(String key, Bitmap value) {        mMemoryCache.put(key, value);    }    @Override    public Bitmap getBitmap(String key) {        return mMemoryCache.get(key);    }    public void clearCache(){        if(mMemoryCache != null){            mMemoryCache.evictAll();        }    }}

package demo.memory.com.memorydemo;import android.widget.ImageView;import com.android.volley.VolleyError;import com.android.volley.toolbox.ImageLoader;public class MyImageListener implements ImageLoader.ImageListener {    private ImageView view;    public MyImageListener(ImageView view){        this.view = view;    }    @Override    public void onErrorResponse(VolleyError error) {}    @Override    public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {        view.setImageBitmap(response.getBitmap());    }}

啟動應用介面

此時app進程佔用記憶體是10M


退出app,點擊進行強制gc回收,但是發現此app雖然退出了,但是還佔用9M記憶體,我們知道一個空的應用進程如果沒有被殺死,它佔用1.4M左右記憶體才算正常的,現在是9M顯然是有問題。接下來用工具分析一下這個問題。

首先抓取此時Heap中的資訊,因為Java中的記憶體佔用主要在Heap中。點擊 進行抓取,大約等幾十秒中,等待抓取完成會產生一個hprof檔案,在Android studio 中的 captures選項夾中可以看到,此檔案會被android studio 自動開啟


Retained Size表示記憶體總佔用,可以看出byte[]佔用了8M多,從這個大小中我們大約可以可以猜測應該是這個的問題,點擊byte[],從右側的Instance中可以找到兩個特別大的對象,



這兩個對象都佔用了4M記憶體,點擊其中一個對象,在下方的Preference Tree中我們可以定位到 com.memorydemo.MyImageCache類的mMemoryCache成員,看來是這個類的問題,從上面代碼中我們可以看到,mMemoryCache是我們定義的一個圖片緩衝對象,為什麼不能被gc回收呢?是因為它是靜態。

再來通過另一個遠古神器MAT來分析一下,MAT http://www.eclipse.org/mat/downloads.php

MAT啟動介面



此工具是讀取hprof檔案的,和上面的一樣,但是Android studio產生的不是標準的需要轉換一下,在Captures裡右鍵選擇 Export to standard.hprof



轉換後的檔案,可以被MAT識別,在MAT的File->Open Heap Dump開啟轉換後的檔案,選擇Leak Suspects Report ,進入介面




我們點擊 此按鈕產生一個histogram,



可以看到同樣是byte[]佔用高,右鍵byte[],--> List objects --> with incoming references



可見是前兩個對象佔用過高,右鍵其中一個對象 --> Path to GC Roots --> exclude all phantom/weak/soft etc. references,進入如下介面點開調用棧發現最終定位到了mImageCache,此對象前面有個小黃點,表示它不能被gc回收,前面說了它是個靜態成員



好了通過這兩個工具清晰的說明了,之所以app退出後仍然沒有釋放記憶體,是因為我們的圖片緩衝沒有釋放導致,那麼我們在何時釋放圖片緩衝了?當然是在程式的全部UI都退出後。Android 提供了 public void onTrimMemory(int level)方法來監聽此過程,在 https://developer.android.com/training/articles/memory.html文檔中的 Release memory as memory becomes tight中講解了 此方法各參數的用法,我們這裡只堅挺 level = TRIM_MEMORY_UI_HIDDEN 的情況,此level表示app所有介面已不可見,在Application類中重寫方法onTrimMemory釋放快取檔案

@Override    public void onTrimMemory(int level) {        super.onTrimMemory(level);        if(level == TRIM_MEMORY_UI_HIDDEN){            MyImageCache.getInstance().clearCache();        }    }
此時app如果退出,此app進程佔用記憶體值就回到了正常狀態

demo代碼下載 http://download.csdn.net/detail/u011291205/9621303

下一篇來說明一下一個記憶體泄露問題


工作中遇到的Android記憶體最佳化問題(1)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.