Android內(nèi)存泄漏檢測和定位

建議閱讀Android常見內(nèi)存泄漏這篇文章,本文的例子來源于文章中的內(nèi)存泄漏典型例子

內(nèi)存泄漏檢測工具

Profiler

其實(shí)Android studio自帶的 Profiler 是不錯(cuò)的,可以很直觀看到CPU、內(nèi)存、網(wǎng)絡(luò)的變化,但是有時(shí)候簡單看看是看不出來內(nèi)存泄漏的,需要知道具體怎么去分析

Android LeakCanary

Android LeakCanary易于集成,自動(dòng)檢測出內(nèi)存泄漏,十分好用

使用Profiler

以Android中的靜態(tài)變量為例

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();
            }
        });
    }

程序是這樣的,第一個(gè)Activity跳轉(zhuǎn)到第二個(gè)Activity,然后finish()返回第一個(gè)Activity,我們反復(fù)多做幾次;正常來講第二個(gè)Activity會(huì)被銷毀的,但是因?yàn)楸混o態(tài)變量引用了,所以應(yīng)該是無法被回收的;

使用Profiler來查看內(nèi)存泄漏

首先是點(diǎn)擊下面那一欄的Profiler按鈕,可能還沒有選擇程序,點(diǎn)擊+添加程序,這一步一般在我們操作程序前做,不然都沒記錄

未標(biāo)題-2.jpg-41.6kB

會(huì)顯示CPU、內(nèi)存、網(wǎng)絡(luò)和能耗四個(gè)東西,點(diǎn)進(jìn)內(nèi)存里面去看詳情信息,其實(shí)只看內(nèi)存的大致情況不能得出什么結(jié)論,感覺好像沒什么問題

未標(biāo)題-2.jpg-51.9kB

我們點(diǎn)擊那個(gè)箭頭符號(Dump Java heap),來捕獲堆轉(zhuǎn)儲(chǔ),堆轉(zhuǎn)儲(chǔ)顯示在您捕獲堆轉(zhuǎn)儲(chǔ)時(shí)您的應(yīng)用中哪些對象正在使用內(nèi)存,選擇按包名排序

未標(biāo)題-3.jpg-115kB

然后選擇我們的程序,就可以看到哪些對象正在使用內(nèi)存

未標(biāo)題-4.jpg-157kB

看見Main2Activity還在內(nèi)存中,證明它沒有被回收掉,內(nèi)存是發(fā)生了泄漏的,其中Main2Activity$1應(yīng)該是表示Main2Activity里面的第一個(gè)匿名內(nèi)部類對Main2Activity的引用,如果還有其他的匿名內(nèi)部類,就是$2、$3這樣排下去;

Heap Dump 右邊四列的意思分別如下,一般情況下,如果Shallow Size和Retained Size都非常小并且相等,都可以認(rèn)為是已經(jīng)被回收的對象。

  • Allocations:Java堆中的實(shí)例個(gè)數(shù)
  • Native Size:native層分配的內(nèi)存大小。
  • Shallow Size:Java堆中分配實(shí)際大小
  • Retained Size:這個(gè)類的所有實(shí)例保留的內(nèi)存總大?。ú⒎菍?shí)際大小)

點(diǎn)擊Heap Dump中的Main2Activity對象,發(fā)現(xiàn)右側(cè)出現(xiàn)了Instance View,再點(diǎn)擊Instance View中的對象,出現(xiàn)Reference和上圖一樣;Reference顯示對這個(gè)Main2Activity對象的引用,大部分都是系統(tǒng)層面的引用,可以看到第一個(gè)是sActivity這個(gè)靜態(tài)變量的引用,就說明是它引起的內(nèi)存泄漏;

屏幕快照 2019-02-13 下午10.25.15.png-136.6kB

還發(fā)現(xiàn)有很多this$0的引用,這個(gè)也往往是導(dǎo)致泄漏的原因,點(diǎn)進(jìn)去查看發(fā)現(xiàn)最終還是sActivity的引用;而出現(xiàn)多個(gè)this$0是因?yàn)槲曳磸?fù)操作了很多遍導(dǎo)致創(chuàng)建了很多個(gè)Main2Activity對象未被回收

在內(nèi)存泄漏檢查的過程中,我發(fā)現(xiàn)經(jīng)常出現(xiàn)過理論上對象肯定是被回收了,卻仍保留的情況。一般情況下,如果Shallow Size和Retained Size都非常小(比如我測試的一個(gè)空的activity,大概是270)并且相等,都可以認(rèn)為是已經(jīng)被回收的對象。因?yàn)橄到y(tǒng)已經(jīng)不認(rèn)為它會(huì)被用到,并且沒有給它保留分配的內(nèi)存。

使用Android LeakCanary

這個(gè)東西特別簡單,直接看官網(wǎng)就行了

就是GitHub地址:https://github.com/square/leakcanary

直接集成:

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中使用,然后運(yùn)行APP就會(huì)自動(dòng)檢測,檢測到會(huì)在另一個(gè)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...
  }
}

舉個(gè)栗子

以匿名內(nèi)部類為例,操作流程和之前的例子一樣;正常來講調(diào)用了finish()方法,第二個(gè)Activity會(huì)被銷毀的,但是因?yàn)槭褂昧四涿麅?nèi)部類,所以sRunnable會(huì)持有Main2Activity的引用,而且sRunnable還是一個(gè)靜態(tài)變量,所以會(huì)導(dǎo)致Main2Activity不會(huì)被回收掉

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();
            }
        });

    }
}

運(yùn)行程序,然后過一會(huì)兒就會(huì)收到提醒,檢測到了內(nèi)存泄漏,打開看看;大概意思就是說sRunnable這個(gè)對象,它引用了Main2Activity,導(dǎo)致了內(nèi)存泄漏;這個(gè)工具的確非常的簡單友好了

未標(biāo)題-6.jpg-47.7kB

參考文章:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容