1. 程式人生 > >Android Studio Memory Monitor

Android Studio Memory Monitor

1.簡介

Memory monitor 可以幫助分析記憶體使用情況。當應用的記憶體佔用很高時,本工具可以幫助檢視類、類的例項,和所有變數所佔用的記憶體。通過分析各部分佔用的記憶體,可以幫助查詢潛在的記憶體洩露,也可以通過分析各變數的記憶體佔用,對程式碼進行優化,減少記憶體的使用,使應用可以在低記憶體高效能下執行。
Memory monitor主要功能:

  • 展示了一個記憶體使用情況的曲線圖
  • 顯示垃圾回收(GC)的時間。
  • 主動觸發一個垃圾回收(GC)

2.使用方法

2.1 記憶體映象

生成記憶體映象,當從圖中發現記憶體使用較高時,點選 可以主動觸發GC。當應用記憶體使用很高,並且存在很嚴重的記憶體洩露時,點選該按鈕後的效果並不明顯,記憶體並沒有降低或者降低的很少。點選

稍等片刻,會生成該應用的記憶體快照。如圖

  • Total Count:記憶體中該類物件個數。
  • Heap Count:堆記憶體中該類物件個數。
  • Sizeof:每個物件的大小(如果為0,則大小不固定)。
  • Shallow size:物件本身佔有記憶體大小。
  • Retained Size:釋放該物件後,節省的記憶體大小。

其中,Retained Size之所以比Shallow size大的原因是,該物件釋放後,會引起其他物件的回收。 例如,某個物件被回收後: 該物件引用的其他物件也會被回收, 該物件A被另一物件B強引用後,之前物件B因為強引用該物件A而沒有被回收,現在該物件A被回收後,若物件B強引用的其他物件都已被回收,則物件B也會被回收。

優化記憶體:
點選Class Name中的類名,檢視其所有例項(Instance),分析例項中引數所佔用的記憶體。

根據Retaine Size 排序,查詢Instance 中Depth較小的例項或者引數,在程式碼中找到相應的位置,檢視記憶體佔用是否合理。

判斷記憶體洩露的步驟:

從Total Count入手。該類的物件數量不對。

  • 很多物件只可能存在1個,若存在多個,則可能存在洩露。例如MainActivity的數量為2。又或者由於單例的使用不規範而導致建立多個“單例”物件。
  • 某個物件已經不再使用,而其還在記憶體中顯示。例如LoginActivity已經退出了,其數量為1。

Memory Monitor只提供了記憶體資訊,如需詳細資訊,可以通過android studio 的Captures (View—–Tool window—–Captures)欄,右鍵點選快照檔案,Export to standard .hprof 將堆快照(Heap Snapshot)轉換成通用的hprof檔案,之後可以通過其他記憶體分析工具開啟,例如MAT。

2.2 跟蹤記憶體分配(Allocation tracker)

點選

開始監聽記憶體的分配情況,再次點選,監聽完成,生成報告。該報告顯示這段時間內,記憶體的分配情況。

2.3 小結:

2.1是從記憶體的靜態資訊中分析,是某一個點的記憶體使用情況。2.2是跟蹤某一段時間內記憶體的分配情況,是個過程跟蹤。分析記憶體可以相結合,例如,再進行某個操作前,執行2.1匯出靜態記憶體資訊,在開啟2.2開始跟蹤記憶體的分配。當執行完操作的時候,關閉記憶體分配的跟蹤,再次執行2.1的,匯出操作某個流程後的靜態資訊。將2.1的兩個靜態表結合2.2的記憶體分配動態過程一起分析。

3.分析樣例

3.1.佔用記憶體分析樣例

開啟某應用後,切換幾個頁面,記憶體飛速上漲。這只是一個極端例子,有很多app,隨著用的時間越長,記憶體也是一直在升高。

根據2.1的方法,生成記憶體映象。

查詢可疑的物件,這個過程是逐個分析的過程,例如byte[]其實是其他物件的某個引數,很多本質上都是byte[],例如Bitmap中mBuffer也是byte[],當記憶體中有很多Bitmap的時候,byte[]也會很高。所以byte[]不是我們重點關注物件,如果真的是因為Bitmap(或者其他類)多而造成的byte[]高,那麼下面肯定會有該類也會佔用很高記憶體。
繼續分析下一個HashMap$HashMapEntry。點選它後,在Instance欄中,看到其例項很多,先從佔用大的例項入手。

例如99=這個,點選它後Reference Tree顯示如下:

得知這個HashMap是ReuseThumbnail…類中ReuseThumbnailManager的REUSE_BITMAPS。在程式碼中檢視其大小是否合理。本例中REUSE_BITMAPS引數是static引數,其型別是HashMap,檢視邏輯,看其是否正常。
再看其他引數,逐個分析其記憶體佔用是否正常。

分析記憶體是個逐步的過程,一個問題解決後,再次迴圈這些步驟。有時候雖然列表中顯示很多物件佔用記憶體很高,有可能是同一個引數導致的,所以一個問題解決後,有可能有一系列引數佔用高的情況會消失。就比如如果高記憶體bitmap的優化後,byte[]也很降低很多,其他引數也有可能會降低很多。

3.2跟蹤記憶體分配分析樣例

3.1是從靜態記憶體資訊中分析記憶體的使用,現在按照2.2從動態過程中跟蹤記憶體的分配。

生成報告如下:

檢視Size最大的一個Thread.

可以看到呼叫過程,從NewDisplayRunnale(執行了636次)呼叫了BitmapDecoder的decode方法(執行了135次),從程式碼中分析過程是否合理。

3.3記憶體洩露分析樣例

對於android的記憶體洩露,一般監測Activity的洩露居多,例如LeakCanary預設也是監測Activity是否洩露。
現寫一個demo,故意造成記憶體洩露作為分析樣例。

public class MyActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        new TestThread().start();
    }

    class TestThread extends Thread{
        @Override
        public void run() {
            super.run();
            try {
                Thread.sleep(10 * 60 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

當進入MyActivity點選回退,重複多次。
按照2.1方法獲取記憶體映象。

看到MyActivity的例項數量為17個。在右邊的Analyzer Tasks欄中:

一般引起Activity洩露是由於其Context被強引用導致的。

MyActivity的context 引數被TestThread引用了。所以在activity的銷燬的時候,由於TestThread還引用著MyActivity,所以阻止了MyActivity被釋放,因而導致記憶體洩露。

參考文獻及其他資料: