1. 程式人生 > >Android效能優化篇之記憶體優化--記憶體洩漏

Android效能優化篇之記憶體優化--記憶體洩漏

文章目錄

介紹

本文主要介紹記憶體洩漏的產生原因分析,常見的導致記憶體洩漏的示例,以及記憶體洩漏優化的方法1

什麼是記憶體洩露

當一個物件已經不需要在使用了,本應該被回收,而另一個正在使用的物件持有它的引用,導致物件不能被回收。因為不能被及時回收的本該被回收的記憶體,就產生了記憶體洩漏。如果記憶體洩漏太多會導致程式沒有辦法申請記憶體,最後出現記憶體溢位的錯誤。

android中導致記憶體洩漏的主要幾種情況

  • 使用單例模式
  • 使用匿名內部類
  • 使用非同步事件處理機制Handler
  • 使用靜態變數
  • 資源未關閉
  • 設定監聽
  • 使用AsyncTask
  • 使用Bitmap

1.單例模式

單例模式是我們在開發過程中經常使用的一種設計模式,但是使用的不當也會造成記憶體洩露的產生,下面舉個例子

private static ComonUtil mInstance = null;
private Context mContext = null;
public ComonUtil(Context context) {
    mContext = context;
}

public static ComonUtil getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new ComonUtil(context);
    }
    return mInstance;
}

我們看到上面的程式碼就是我們平時使用的單例模式,當然這裡沒有考慮執行緒安全,請忽略。當我們傳遞進來的是Context,那麼當前物件就會持有第一次例項化的Context,如果Context是Activity物件,那麼就會產生記憶體洩漏。因為當前物件ComonUtil是靜態的,生命週期和應用是一樣的,只有應用退出才會釋放,導致Activity不能及時釋放,帶來記憶體洩漏。

怎麼解決呢?
常見的有兩種方式,第一就是傳入ApplicationContext,第二CommonUtil中取context.getApplicationContext()。

public ComonUtil(Context context) {
    mContext = context.getApplicationContext();
}

2.使用非靜態內部類

/**
 * 非靜態內部類
 */
public void createNonStaticInnerClass(){
    CustomThread mCustomThread = new CustomThread();
    mCustomThread.start();
}

public class CustomThread extends Thread{
    @Override
    public void run() {
        super.run();
        while (true){
            try {
                Thread.sleep(5000);
                Log.i(TAG,"CustomThread ------- 列印");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

我們就以執行緒為例,當Activity呼叫了createNonStaticInnerClass方法,然後退出當前Activity時,因為執行緒還在後臺執行且當前執行緒持有Activity引用,只有等到執行緒執行完畢,Activitiy才能得到釋放,導致記憶體洩漏。
常用的解決方法有很多,第一把執行緒類宣告為靜態的類,如果要用到Activity物件,那麼就作為引數傳入且為WeakReference,第二在Activity的onDestroy時,停止執行緒的執行。

public static class CustomThread extends Thread{
    private WeakReference<MainActivity> mActivity;
    public CustomThread(MainActivity activity){
        mActivity = new WeakReference<MainActivity>(activity)
    }
}

3.使用非同步事件處理機制Handler

/**
 * 非同步訊息處理機制  -- handler機制
 */
public void createHandler(){
    mHandler.sendEmptyMessage(0);
}
public Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        //處理耗時操作   
        return false;
    }
});

這個應該是我們平時使用最多的一種方式,如果當handler中處理的是耗時操作,或者當前訊息佇列中訊息很多時,那當Activity退出時,當前message中持有handler的引用,handler又持有Activity的引用,導致Activity不能及時的釋放,引起記憶體洩漏的問題。
解決handler引起的記憶體洩漏問題常用的兩種方式:
1.和上面解決Thread的方式一樣,
2.在onDestroy中呼叫mHandler.removeCallbacksAndMessages(null)

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

4.使用靜態變數

同單例引起的記憶體洩漏。

5.資源未關閉

常見的就是資料庫遊標沒有關閉,物件檔案流沒有關閉,主要記得關閉就OK了。

6.設定監聽

常見的是在觀察者模式中出現,我們在退出Acviity時沒有取消監聽,導致被觀察者還持有當前Activity的引用,從而引起記憶體洩漏。
常見的解決方法就是在onPause中注消監聽

7.使用AsyncTask

public AsyncTask<Object, Object, Object> mTask = new AsyncTask<Object, Object, Object>() {

    @Override
    protected Object doInBackground(Object... params) {
        //耗時操作
        return null;
    }

    @Override
    protected void onPostExecute(Object o) {
    
    }   
};

和上面同樣的道理,匿名內部類持有外部類的引用,AsyncTask耗時操作導致Activity不能及時釋放,引起記憶體洩漏。
解決方法同上:
1.宣告為靜態類,
2.在onPause中取消任務

8.使用Bitmap

我們知道當bitmap物件沒有被使用(引用),gc會回收bitmap的佔用記憶體,當時這邊的記憶體指的是java層的,那麼本地記憶體的釋放呢?我們可以通過呼叫bitmap.recycle()來釋放C層上的記憶體,防止本地記憶體洩漏


  1. 轉載說明:
    連結:https://www.jianshu.com/p/797395731747 ↩︎