Android內存泄漏檢測和定位

內存泄漏檢測工具

Profiler

其實Android studio自帶的 Profiler 是不錯的,能夠很直觀看到CPU、內存、網絡的變化,可是有時候簡單看看是看不出來內存泄漏的,須要知道具體怎麼去分析android

Android LeakCanary

Android LeakCanary易於集成,自動檢測出內存泄漏,十分好用git

使用Profiler

以Android中的靜態變量爲例github

private static Activity sActivity;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        sActivity = this;
        findViewById(R.id.btn_back).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
    }
複製代碼

程序是這樣的,第一個Activity跳轉到第二個Activity,而後finish()返回第一個Activity,咱們反覆多作幾回;正常來說第二個Activity會被銷燬的,可是由於被靜態變量引用了,因此應該是沒法被回收的;bash

使用Profiler來查看內存泄漏

首先是點擊下面那一欄的Profiler按鈕,可能尚未選擇程序,點擊+添加程序,這一步通常在咱們操做程序前作,否則都沒記錄網絡

會顯示CPU、內存、網絡和能耗四個東西,點進內存裏面去看詳情信息,其實只看內存的大體狀況不能得出什麼結論,感受好像沒什麼問題

咱們點擊那個箭頭符號(Dump Java heap),來捕獲堆轉儲,堆轉儲顯示在您捕獲堆轉儲時您的應用中哪些對象正在使用內存,選擇按包名排序

而後選擇咱們的程序,就能夠看到哪些對象正在使用內存

看見Main2Activity還在內存中,證實它沒有被回收掉,內存是發生了泄漏的,其中Main2Activity$1應該是表示Main2Activity裏面的第一個匿名內部類對Main2Activity的引用,若是還有其餘的匿名內部類,就是$二、$3這樣排下去;

Heap Dump 右邊四列的意思分別以下,通常狀況下,若是Shallow Size和Retained Size都很是小而且相等,均可以認爲是已經被回收的對象。app

  • Allocations:Java堆中的實例個數
  • Native Size:native層分配的內存大小。
  • Shallow Size:Java堆中分配實際大小
  • Retained Size:這個類的全部實例保留的內存總大小(並不是實際大小)

點擊Heap Dump中的Main2Activity對象,發現右側出現了Instance View,再點擊Instance View中的對象,出現Reference和上圖同樣;Reference顯示對這個Main2Activity對象的引用,大部分都是系統層面的引用,能夠看到第一個是sActivity這個靜態變量的引用,就說明是它引發的內存泄漏;ide

還發現有不少this$0的引用,這個也每每是致使泄漏的緣由,點進去查看發現最終仍是sActivity的引用;而出現多個this$0是由於我反覆操做了不少遍致使建立了不少個Main2Activity對象未被回收

在內存泄漏檢查的過程當中,我發現常常出現過理論上對象確定是被回收了,卻仍保留的狀況。通常狀況下,若是Shallow Size和Retained Size都很是小(好比我測試的一個空的activity,大概是270)而且相等,均可以認爲是已經被回收的對象。由於系統已經不認爲它會被用到,而且沒有給它保留分配的內存。工具

使用Android LeakCanary

這個東西特別簡單,直接看官網就好了測試

就是GitHub地址:github.com/square/leak…ui

直接集成:

dependencies {
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
  releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
  // Optional, if you use support library fragments:
  debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
}
複製代碼

直接在Application中使用,而後運行APP就會自動檢測,檢測到會在另外一個APP上通知,顯示詳情

public class ExampleApplication extends Application {

  @Override public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // This process is dedicated to LeakCanary for heap analysis.
      // You should not init your app in this process.
      return;
    }
    LeakCanary.install(this);
    // Normal app init code...
  }
}
複製代碼

舉個栗子

以匿名內部類爲例,操做流程和以前的例子同樣;正常來說調用了finish()方法,第二個Activity會被銷燬的,可是由於使用了匿名內部類,因此sRunnable會持有Main2Activity的引用,並且sRunnable仍是一個靜態變量,因此會致使Main2Activity不會被回收掉

public class Main2Activity extends AppCompatActivity {
    private static Thread sRunnable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        sRunnable = new Thread() {
            @Override
            public void run() {
            }
        };

        findViewById(R.id.btn_back).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });

    }
}
複製代碼

運行程序,而後過一下子就會收到提醒,檢測到了內存泄漏,打開看看;大概意思就是說sRunnable這個對象,它引用了Main2Activity,致使了內存泄漏;這個工具的確很是的簡單友好了

若是喜歡本文的話,歡迎點擊一下 「喜歡」 點個贊給予鼓勵支持!
相關文章
相關標籤/搜索