Android內存優化及內存泄漏檢測

Android內存的優化幾個方面

  • 靜態變量引起內存泄露
    在代碼優化的過程中,我們需要對代碼中的靜態變量特別留意。靜態變量是類相關的變量, 它的生命週期是從這個類被聲明,到這個類徹底被垃圾回收器回收纔會被銷燬。所以,一般情況下,靜態變量從所在的類被使用開始就要一直佔用着內存空間,直到 程序退出。如果不注意,靜態變量引用了佔用大量內存的資源,造成垃圾回收器無法對內存進行回收,就可能造成內存的浪費
  • 使用Application的Context
    在Android中,Application Context的生命週期和應用的生命週期一樣長,而不是取決於某個Activity的生命週期。如果想保持一個長期生命的對象,並且這個對象需要一個 Context,就可以使用Application對象。可以通過調用Context.getApplicationContext()方法或者 Activity.getApplication()方法來獲得Application對象。
  • 及時關閉資源
    Cursor是Android查詢數據後得到的一個管理數據集合的類。正常情況下,如 果我們沒有關閉它,系統會在回收它時進行關閉,但是這樣的效率特別低。如果查詢得到的數據量較小時還好,如果Cursor的數據量非常大,特別是如果裏面 有Blob信息時,就可能出現內存問題。所以一定要及時關閉Cursor。
  • 使用Bitmap及時調用recycle() // 把 重複循環的方法設置 爲 null,釋放內存
    前面的章節講過,在不使用Bitmap對象時,需要調用recycle()釋放內存,然後將它設置爲null。雖然調用recycle()並不能保證立即釋放佔用的內存,但是可以加速Bitmap的內存的釋放。
    在代碼優化的過程中,如果發現某個Activity用到了Bitmap對象,卻沒有顯式的調用recycle()釋放內存,則需要分析代碼邏輯,增加相關代碼,在不再使用Bitmap以後調用recycle()釋放內存。
  • 對Adapter進行優化
    下面以構造ListView的BaseAdapter爲例說明如何對Adapter進行優化。
    @軟引用和弱引用。
    如果一個對象只具有軟引用,那麼如果內存空間足夠,垃圾回收器就不會回收它;如果內存 空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存。軟引用可以和一個引用隊 列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
    如果一個對象只具有弱引用,那麼在垃圾回收器線程掃描的過程中,一旦發現了只具有弱引 用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由於垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。弱 引用也可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯 的引用隊列中。
    弱引用與軟引用的根本區別在於:只具有弱引用的對象擁有更短暫的生命週期,可能隨時被回收。而只具有軟引用的對象只有當內存不夠的時候才被回收,在內存足夠的時候,通常不被回收。
    UI優化
    在Android應用開發過程中,屏幕上控件的佈局代碼和程序的邏輯代碼通常是分開 的。界面的佈局代碼是放在一個獨立的xml文件中的,這個文件裏面是樹型組織的,控制着頁面的佈局。通常,在這個頁面中會用到很多控件,控件會用到很多的 資源。Android系統本身有很多的資源,包括各種各樣的字符串、圖片、動畫、樣式和佈局等等,這些都可以在應用程序中直接使用。這樣做的好處很多,既 可以減少內存的使用,又可以減少部分工作量,也可以縮減程序安裝包的大小。

Android內存泄漏如何檢測

  • Eclipse工具檢測
  • Studio檢測
  • LeakCanary檢測

LeakCanary檢測原理

參考文章 https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/


工作機制

RefWatcher.watch() 創建一個 KeyedWeakReference 到要被監控的對象。

然後在後臺線程檢查引用是否被清除,如果沒有,調用GC。

如果引用還是未被清除,把 heap 內存 dump 到 APP 對應的文件系統中的一個 .hprof 文件中。

在另外一個進程中的 HeapAnalyzerService 有一個 HeapAnalyzer 使用HAHA 解析這個文件。

得益於唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位內存泄露。

HeapAnalyzer 計算 到 GC roots 的最短強引用路徑,並確定是否是泄露。如果是的話,建立導致泄露的引用鏈。

引用鏈傳遞到 APP 進程中的 DisplayLeakService, 並以通知的形式展示出來。

本文主要介紹LeakCanary檢測方式

  • 編譯配置
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
  • 代碼入口配置
public class ExampleApplication extends Application {

    private RefWatcher mRefWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        mRefWatcher = LeakCanary.install(this);
    }

}
  • 來一個內存泄漏測一下,在單例類中持有上下文的引用
public class SingleInstance {
    private static SingleInstance sInstance;
    private final Context context;
    private SingleInstance(Context context){
        this.context=context;
    }
    public static SingleInstance getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new SingleInstance(context);
        }
        return sInstance;
    }
}
  • 在Activity中調用一下
 SingleInstance.getInstance(this);
  • 退出Activity,數上20秒,看一下logcat 會有對應的泄漏分析,同時手機會彈出通知,通知如下圖,點開+號就能夠分析出哪裏內存泄漏了。
    這裏寫圖片描述

  • 內存泄漏解決,在單例類中,如果獲取上下文的話,要獲取和生命週期長的app的Context,單個Activity的上下文隨着Activity銷燬就銷燬了…

public class SingleInstance {
    private static SingleInstance sInstance;
    private final Context context;
    private SingleInstance(Context context){
        this.context=context;
    }
    public static SingleInstance getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new SingleInstance(context.getApplicationContext());
        }
        return sInstance;
    }
}
  • 搞定了對Activity的內存泄漏的檢測

檢測Fragment內存泄漏

public abstract class BaseFragment extends Fragment {

  @Override public void onDestroy() {
    super.onDestroy();
    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
    refWatcher.watch(this);
  }
}
發佈了62 篇原創文章 · 獲贊 132 · 訪問量 55萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章