Android內存優化(四)解析Memory Monitor、Allocation Tracker和Heap Dump

前言

要想做好內存優化工作,就要掌握兩大部分的知識,一部分是知道并理解內存優化相關的原理,另一部分就是善于運用內存分析的工具。本篇就來介紹內存分析工具:Memory Monitor、Allocation Tracker和Heap Dump的使用方法。

1.Memory Monitor

在Android Studio(以下簡稱AS)中Android Monitor是一個主窗口,它包含了Logcat,、Memory Monitor、CPU Monitor、 GPU Monitor和Network Monitor。其中Memory Monitor可以輕松地監視應用程序的性能和內存使用情況,以便于找到被分配的對象,定位內存泄漏,并跟蹤連接設備中正在使用的內存數量。Memory Monitor可以報告出你的應用程序的內存分配情況, 更形象的呈現出應用程序使用的內存。它的作用如下:

  • 實時顯示可用的和分配的Java內存的圖表。
  • 實時顯示垃圾收集(GC)事件。
  • 啟動垃圾收集事件。
  • 快速測試應用程序的緩慢是否與過度的垃圾收集事件有關。
  • 快速測試應用程序崩潰是否與內存耗盡有關。

1.1 使用Memory Monitor

在使用Memory Monitor之前要確保手機開啟了開發者模式和USB調試。
使用的步驟為:
1.運行需要監控的應用程序。
2.點擊AS面板下面的Android圖標,并選擇Monitors選項。
如果Memory Monitor已經運行,效果如下圖所示(AS版本2.3.2)。


圖中的標注的功能如下:

  • Initiate GC(標識1):用來手動觸發GC。
  • Dump Java heap(標識2):保存內存快照。
  • Start/Stop Allocation Tracking(標識3):打開Allocation Tracker工具(后面會介紹)。
  • Free(標識4):當前應用未分配的內存大小。
  • Allocated(標識5):當前應用分配的內存大小。

圖中y軸顯示當前應用的分配的內存和未分配的內存大小;x軸表示經過的時間。

1.2 大內存申請與GC

am-gc2.png

從上圖可以看出,分配的內存急劇上升,這就是大內存分配的場景,我們要判斷這是否是合理的分配的內存,是Bitmap還是其他的大數據,并且對這種大數據進行優化,減少內存開銷。
接下來分配的內存出現急劇下降,這表示垃圾收集事件,用來釋放內存。

1.3 內存抖動

內存抖動一般指在很短的時間內發生了多次內存分配和釋放,嚴重的內存抖動還會導致應用程序卡頓。內存抖動出現原因主要是短時間頻繁的創建對象(可能在循環中創建對象),內存為了應對這種情況,也會頻繁的進行GC,因此綜合起來就產生了內存抖動,產生了如上圖般的鋸齒狀。

2.Allocation Tracker

Allocation Tracker用來跟蹤內存分配,它允許你在執行某些操作的同時監視在何處分配對象,了解這些分配使你能夠調整與這些操作相關的方法調用,以優化應用程序性能和內存使用。
Allocation Tracker能夠做到如下的事情:

  • 顯示代碼分配對象類型、大小、分配線程和堆棧跟蹤的時間和位置。
  • 通過重復的分配/釋放模式幫助識別內存變化。
  • 當與 HPROF Viewer結合使用時,可以幫助你跟蹤內存泄漏。例如,如果你在堆上看到一個bitmap對象,你可以使用Allocation Tracker來找到其分配的位置。

2.1 使用Allocation Tracker

AS和DDMS中都有Allocation Tracker,這里會·介紹AS中的Allocation Tracke如何使用。首先要確保要確保手機開啟了開發者模式,并且開啟了USB調試。
使用的步驟為:
1.運行需要監控的應用程序。
2.點擊AS面板下面的Android圖標,并選擇Monitors選項。
3.點擊Start Allocation Tracking按鈕,這時Start Allocation Tracking按鈕變為了Stop Allocation Tracking按鈕。
4.操作應用程序。
5.點擊Stop Allocation Tracking按鈕,結束快照。這時Memory Monitor會顯示出捕獲快照的期間,如下圖所示。


6.過幾秒后就會自動打開一個窗口,顯示當前生成的alloc文件的內存數據。

2.2 alloc文件分析

自動打開的alloc文件窗口如下圖所示。


該alloc文件顯示以下信息:

| 列 | 說明|
| :-------- :|: --------:|
| Method | 負責分配的Java方法|
| Count| 分配的實例總數|
| Total Size| 分配內存的總字節數|

接著我們來分析標紅框的內容,負責分配的Java方法為performLaunchActivity,內存分配序列為2369,分配的對象為ActivityThread,分配的實例總數為300個,分配內存的總字節數為10512。不了解performLaunchActivity方法和ActivityThread可以看Android深入四大組件這一系列的文章。

目前的菜單選項是Group by Method我們也可以選擇 Group By Allocator,如下圖所示。


為了更好的解釋圖中的信息,這里給出測試的代碼,MainActivity和SecondActivity 的代碼如下所示。
MainActivity.java

public class MainActivity extends AppCompatActivity {
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button =(Button)findViewById(R.id.bt_next);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this,SecondActivity.class));
            }
        });
    }
}

SecondActivity.java

public class SecondActivity extends AppCompatActivity {
    private static Object inner;
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.bt_next);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                createInnerClass();
                finish();
            }
        });
    }
    void createInnerClass() {
        class InnerClass {
        }
        inner = new InnerClass();
    }
}

其中SecondActivity是存在內存泄漏的,生成快照期間,我的操作就是在MainActivity和SecondActivity跳轉了3次(點擊button 共6次)。這時我們回過頭來看上圖的紅框的信息,MainActivity總共分配了3個Intent實例,占用內存為192字節。SecondActivity總共分配了6個實例,占用內存為96字節,其中分配了3個匿名內部類OnClickListener的實例,3個InnerClass的實例。

我們可以選擇列表中的一項,單擊鼠標右鍵,在彈出的菜單中選擇jump to the source就可以跳轉到對應的源文件中。
除此之外,還可以點擊Show/Hide Chart按鈕來顯示數據的圖形化,如下圖所示。


3.Heap Dump

Heap Dump的主要功能就是查看不同的數據類型在內存中的使用情況。它可以幫助你找到大對象,也可以通過數據的變化發現內存泄漏。

3.1 使用Heap Dump

打開Android Device Monitor工具,在左邊Devices列表中選擇要查看的應用程序進程,點擊Update Heap按鈕(裝有一半綠色液體的圓柱體),在右邊選擇Heap選項,并點擊Cause GC按鈕,就會開始顯示數據。我們每次點擊Cause GC按鈕都會強制應用程序進行垃圾回收,并將清理后的數據顯示在Heap工具中。如下圖所示。

從上圖可以看出,Heap工具共有三個區域,分別是總覽視圖(標識1)、詳情視圖(標識2)和內存分配柱狀圖(標識2)。

3.2 總覽視圖

其中總覽視圖可以查看整體的內存情況,表中的顯示信息如下所示。

|列 | 說明 |
| :-------- :|: --------:|
| Heap Size|堆棧分配給該應用程序的內存大小 |
| Allocated|已分配使用的內存大小 |
| Free|空閑的內存大小 |
| %Used|當前Heap的使用率(Allocated/Heap Size) |
| #Objects|對象的數量 |

結合上表和上圖,我們在總覽視圖獲得的信息就是:堆棧分配給當前的應用程序的內存大小為2.346MB,已分配的內存為1.346MB,空閑的內存為1MB,當前Heap的使用率為57.37%,對象的數量為24058個。

3.3 詳情視圖

詳細視圖展示了所有的數據類型的內存情況,表中列的信息如下所示。

|列 | 說明 |
| :-------- :|: --------:|
| Type|數據類型 |
| Total Size|總共占用的內存大小 |
| Smallest|將該數據類型的對象從小到大排列,排在第一個的對象所占用的內存 |
| Largest|將該數據類型的對象從小到大排列,排在最后一個的對象所占用的內存 |
| Median|將該數據類型的對象從小到大排列,排在中間的對象所占用的內存 |
| Average|該數據類型的對象所占用內存的平均值 |

除了列的信息,還有行信息:

|行 | 說明 |
| :-------- :|: --------:|
| free|內存碎片|
| data object|對象 |
| class object|類 |
| 1-byte array (byte[],boolean[])|1字節的數組對象 |
| 2-byte array (short[],char[])|2字節的數組對象 |
| 4-byte array (object[],int[],float[])|4字節的數組對象 |
| 6-byte array (long[],double[])|8字節的數組對象 |
| non-Java object|非Java對象 |

行信息中比較重要的是free,它與總覽視圖中的free的含義不同,它代表內存碎片。當新創建一個對象時,如果碎片內存能容下該對象,則復用碎片內存,否則就會從free空間(總覽視圖中的free)重新劃分內存給這個新對象。free是判斷內存碎片化程度的一個重要的指標。
此外,1-byte array這一行的信息也很重要,因為圖片是以byte[]的形式存儲在內存中的,如果1-byte array一行的數據過大,則需要檢查圖片的內存管理了。

3.4 檢測內存泄漏

Heap Dump也可以檢測內存泄漏。在左邊Devices列表中選擇要查看的應用程序進程,點擊Update Heap按鈕(裝有一半綠色液體的圓柱體),在右邊選擇Heap選項,并點擊Cause GC按鈕,就會開始顯示數據,如下圖所示。

這時data object的Total Size為270.266KB。接下來操作應用,這個應用仍舊是在2.2小節所舉的內存泄漏的例子,我反復的在MainActivity和SecondActivity跳轉了10次(點擊Button共20次),數據顯示為:


data object的Total Size變為了768.172KB。這時我點擊Cause GC按鈕,數據顯示為:

可以看到data object的Total Size變為了444.516KB,再點擊一次Cause GC按鈕:


Total Size變為了323.312KB,經過兩次Cause GC的操作,Total Size的值從768.172KB變為了323.312KB,這是一個比較大的變化,說明在Cause GC操作之前有462.86KB(768.172KB-323.312KB)的內存沒有被回收,可能發生了內存泄漏。

參考資料
Memory Monitor
Allocation Tracker
Android Monitor Basics
Android性能專項測試之Memory Monitor工具
《Android應用性能優化最佳實踐》
《Android群英傳 神兵利器》
《高性能Android應用開發》

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容