(Android Studio 3.0)Android Profiler記憶體洩漏檢查

許佳佳233發表於2017-12-13

前提概要

記憶體洩漏是常見又重要的問題,針對這個問題谷歌在Android Studio 3.0中推出了Android Profiler。筆者此篇文章主要記錄一下Android Profiler在記憶體洩漏方面的使用。

Android Profiler

這裡寫圖片描述

Android Profiler在Android Studio左下角,需要在Android Studio 3.0及其以上才會有。如果是Android Studio 3.0並且也未有這個按鈕,讀者也不用著急,執行一下自己的專案就會出現。當點選MEMORY那一行的時候就能進入記憶體檢查的介面。

這裡寫圖片描述

接下來筆者通過分析記憶體洩漏的例項的方式來介紹Android Profiler的使用。

例項

這裡寫圖片描述

主要是三個Activity:MainActivity,ActivityOne,ActivityTwo。
MainActivity:主Activity,用於開啟記憶體洩漏的兩個Activity。
ActivityOne:通過handler方式洩漏。
ActivityTwo:通過靜態引用方式洩漏。

程式碼如下:
MainActivity:

public class MainActivity extends AppCompatActivity {

    public static Activity activity;

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

        initView();
    }

    private void initView() {
        Button btnOne=findViewById(R.id.btn_one);
        btnOne.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent(MainActivity.this,ActivityOne.class);
                startActivity(intent);
            }
        });
        Button btnTwo=findViewById(R.id.btn_two);
        btnTwo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent(MainActivity.this,ActivityTwo.class);
                startActivity(intent);
            }
        });
    }
}

ActivityOne:

public class ActivityOne extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {

            }
        },1000000);
    }
}

ActivityTwo:

public class ActivityTwo extends Activity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MainActivity.activity=this;
    }
}

記憶體洩漏分析

操作

首先我們開啟MainActivity,分別開啟ActivityOne和ActivityTwo並退出,回到MainActivity。接著開啟Android Profiler。

檢查記憶體洩漏物件

首先要點選左上方的“Dump Java heap”按鈕。(如果是檢查記憶體洩漏,筆者建議在點選之前先點選垃圾回收按鈕,以防可回收的存貨物件的混淆)
這裡寫圖片描述

然後就會顯示此刻的JAVA堆中物件以及引用情況,我們可以在Heap Dump的右上角選擇物件的排列方式,筆者比較推薦按報名排序,因為一般我們檢查的都是自己所寫的類的洩漏,而非系統層的。
這裡寫圖片描述

如圖,我們很快就發現ActivityOne和ActivityTwo洩漏了。
筆者開啟了ActivityOne和ActivityTwo之後,回到MainActivity介面並按下垃圾回收按鈕。不洩露的情況應該是隻有MainActivity被分配了記憶體,而ActivityOne和ActivityTwo均存活,說明記憶體沒有被釋放,即記憶體洩漏了。

Heap Dump 右邊四列的意思分別如下:
Alloc Count:Java堆中的例項個數
Native Size:native層分配的記憶體大小。
Shallow Size:Java堆中分配實際大小
Retained Size:這個類的所有例項保留的記憶體總大小(並非實際大小)

在記憶體洩漏檢查的過程中,筆者也出現過理論上物件應該被回收,卻仍保留的情況。一般情況下,如果Shallow Size和Retained Size都非常小並且相等,都可以認為是已經被回收的物件。因為系統已經不認為它會被用到,並且沒有給它保留分配的記憶體。

解決記憶體洩漏(方法一)

繼續我們在Heap Dump介面的操作,以檢查ActivityOne為例,我們單擊它,發現右側出現了Instance View,然後單擊Instance View的物件。
這裡寫圖片描述

在Instance View中,會顯示在ActivityOne中的各種物件,而它下方的Reference則是顯示諸多對這個存貨的ActivityOne物件的引用。大部分都是系統層面的引用,只有一個格外顯眼,就是通過“this”對ActivityOne的引用,點進去我們可以發現是MessageQueue持有了這個引用,有點經驗的Android程式設計師馬上可以定位到是Handler的記憶體洩漏了。

解決記憶體洩漏(方法二)

第一種記憶體洩漏的檢查方法由於有過多的系統引用的混淆,相信並不讓人覺得容易上手。這時候相信讀者會想嘗試第三個錄製按鈕了。
Record memory allocations:
這個按鈕的作用是記錄一段時間內的記憶體分配的內容,點選紅色的小圓表示開始錄製,點選小正方形是結束錄製。(錄製時間不建議超過10s,計算記憶體會很慢)
這裡寫圖片描述

操作

首先重新執行APP,停留在MainActivity介面,然後點選紅色小圓按鈕開始錄製,接著分別開啟ActivityOne和ActivityTwo然後退出,回到MainActivity介面,最後點選小正方形結束錄製。

解決記憶體洩漏

然後我們仍然是按照包名排列,找到記憶體洩漏的物件。
這裡寫圖片描述

然後我們選擇ActivityOne,再單擊Instance View 中的這個物件。我們可以發現,完全能再程式碼中追蹤到這個引用建立的地方。
這裡寫圖片描述

我們雙擊Call Stack中的第一行,發現可以直接跳轉到程式碼記憶體洩漏的地方。
這裡寫圖片描述

兩者優劣

解決記憶體洩漏方法一:
1、可以用於檢查記憶體洩漏,並不僅僅是檢視引用情況。
2、不需要定位引用的建立時間,因為檢視的是java堆該時的狀態。
3、不可以定位到相關程式碼。
4、系統引用也會顯示,容易混淆。

解決記憶體洩漏方法二:
1、可以直接定位到相關程式碼。
2、不會有過多的系統引用混淆。
3、需要定位物件建立的時間,在記憶體記錄的時間內進行操作才會顯示。

總結

Android Profiler只是解決記憶體洩漏的一個工具,在一些情況下無法定位到相關程式碼。比如以下情況:

A a=A();
……
c=a;

在這種情況下,如果A類洩漏,那麼程式碼只能定位到 “A a=A();”,而下面的間接引用卻無法由Android Profiler體現出來,這就需要讀者通過閱讀原始碼來自行解決了。

總之也算是孰能生巧,新工具固然能提高我們的效率,但是還是無法替代手動檢查的工作。

相關文章