Android内存分析工具:Memory Profiler
一、前言?
我們知道,Android系統檢測到app有不再使用對象時,就會進行內存回收相關的工作。
盡管Android檢測無用對象、回收內存的方法在不斷改進,?
但在目前所有的Android版本中,進行上述工作時,系統仍需要短暫地停止app的運行。
在大多數情況下,系統進行內存回收的行為是無法被用戶察覺到的。?
然而,如果應用分配內存的速度大于系統回收的速度,?
那么app進程的正常運行可能就回受到影響。
畢竟,系統必須回收到足夠的供app需要的內存,才會恢復處于暫停狀態的app。?
在這種情況下,app就可能出現掉幀、卡頓等現象。
在更嚴重的情況下,如果出現了內存泄露的問題,那么系統中就可能堆積無法釋放的內存,?
使得系統必須更加頻繁地進行內存回收,從而降低系統的性能。
甚至在極端條件下,系統不得不殺死部分正在后臺運行的app進程。?
于是用戶將后臺應用移到前臺時,卻發現應用無故重啟,這顯然帶來了較差的用戶體驗。
由此可見,內存對于app而言,是極其關鍵的性能指標。?
目前,分析app內存的工具有很多,?
本文主要記錄一下Android Studio內置的內存分析工具Memory Profiler。
二、基本介紹?
Memory Profiler是Android Profiler的一個組件, 用于幫助分析內存泄露和內存抖動的問題。?
當PC連接Android L以上的設備時,該工具才能夠正常使用。
Memory Profiler的功能包括:?
展示應用內存使用情況的實時圖像、抓取內存的dump信息、強制垃圾回收及追蹤內存分配。
2.1 開啟步驟?
打開Memory Profiler的步驟為:?
1、?依次點擊Android Studio的View → Tool Windows → Android Profiler,?
或直接點擊工具欄Android Profiler對應的圖標;
2、?PC連接Android終端后,在Android Profiler對應的區域選擇接的設備和需要監控的進程:?
3、?點擊Android Profiler界面中MEMORY區域的任意位置,即可開啟Memory Profiler,如下圖所示:?
需要注意的是,如果PC連接Android 7.1以下的設備時,有些關鍵數據可能無法被Android Profiler統計,?
此時Android Profiler會顯示如下信息:?
這時我們需要依次點擊Android Studio的Run → Edit Configurations → Profiling 按鍵,選中app后點擊Enabled advanced profiling,?
如下圖所示:?
?
為了支持該功能,要求app對應的gradle版本必須在2.4以上。
2.2 界面介紹?
打開Memory Profiler后,主界面如下所示(為了方便,這里直接盜取Android技術文檔中的圖):?
?
其中:?
標注1對應的按鍵用于強制內存回收。
標注2對應的按鍵用于抓取進程內存的dump信息。
標注3對應的按鍵用于記錄內存的分配信息(連接Android 7.1及以下才會有此按鍵)。?
初次點擊時,對應統計的開始時間點;再次點擊時,對應統計的結束時間點。?
進程在兩個時間點之間的內存分配信息,將被Memory Profiler記錄和分析。
標注4對應的區域用于縮放時間軸。
標注5對應的按鍵用于顯示實時的內存數據。
標注6對應的區域用于記錄事件發生的時間點及大致持續的時間(例如activity狀態改變、用戶操作界面等事件)。
標注7對應的區域用于顯示內存使用情況對應的時間軸(與標注6結合,就可以看出各事件帶來的內存變化情況)。?
需要說明的是,標注7對應區域顯示的內容包括:?
不同類型內存占用情況對應的圖像;?
分配對象數量對應的短畫線;?
內存回收事件發生的時機。
2.3 統計的數據類型及含義?
Memory Profiler主要根據Android系統提供的信息,?
統計app獨自占用內存,即不統計app與系統或其它app共有的內存。
Memory Profiler統計內存的種類如下圖所示:?
?
如上圖所示,其中:?
Java表示Java代碼或Kotlin代碼分配的內存;
Native表示C或C++代碼分配的內存(即使App沒有native層,調用framework代碼時,也有可能觸發分配native內存);
Graphics表示圖像相關緩存隊列占用的內存;
Stack表示native和java占用的棧內存;
Code表示代碼、資源文件、庫文件等占用的內存;
Others表示無法明確分類的內存;
Allocated表示Java或Kotlin分配對象的數量(Android8.0以下時,僅統計Memory Profiler啟動后,進程再分配的對象數量;?
8.0以上時,由于系統內置了統計工具,Memory Profiler可以得到整個app啟動后分配對象的數量)。
三、基本用法?
對Memory Profiler有了基本的了解后,我們來看看它的基本用法。
3.1 查看內存分配情況?
Memory Profiler可以查看兩個時間點之間的內存分配情況,包括:?
對象的類型、占用內存的大小、棧信息等。?
連接8.0以上的設備時,Memory Profiler還可以顯示對象被回收的時間。
PC連接8.0以上的設備時,在內存統計的時間線上,直接點擊和拖動就可以選擇觀察區域;?
連接低版本的設備時,則需要點擊Record Memory allocations按鍵(2.2小結介紹的標注3)選擇觀察區域。
選定觀察區域后, Memory Profiler就可以統計這段時間內app分配內存的情況:?
?
從圖中可以看出,Memory Profiler可以顯示分配對象的類名;?
點擊類后,會在Instance View顯示具體的對象;?
點擊具體對象后,會在Call back區域顯示調用棧。?
點擊調用棧信息后,就會跳轉到具體的代碼。
3.2 查看內存占用情況?
點擊2.2小結介紹的標注2,即可抓取點擊后一段時間內app占用內存的dump信息。
通過dump信息,我們可以看到app當前仍存在于內存中的對象。?
結合代碼,我們可以分析是否有本應被析構卻仍存活的泄露對象。
與統計內存分配信息一樣,內存占用信息同樣會顯示對象的類型、數量、占用內存的大小、引用關系等。?
如下圖所示:?
?
圖中Alloc Count表示堆中分配對象的數量;?
Shallow Size表示對象使用Java內存的大小,單位為byte;?
Retained Size表示對象占用的實際內存大小,大于等于Shallow Size;?
7.0及以上版本的設備,還會顯示對象占用的Native Size。
點擊具體的對象時,也會顯示Instance View。?
此時,Instance View顯示的信息變多了,包括:?
Depth表示當前對象到任一GC root的最短跳數;?
Shallow Size、Retained Size的含義與前文一致。?
同樣,7.0及以上版本的設備,還會顯示對象占用的Native Size。
從圖中可以看出,Instance View不會顯示棧信息。?
如果想獲得棧信息的話,必須先點擊Record Memory allocations按鍵。
四、使用示例?
利用Memory Profiler,我分析了一下某反病毒引擎SDK的內存占用情況。?
隨便寫了個demo,繼承SDK后啟動app,內存占用情況如下圖所示:?
?
然后,通過操作UI初始化SDK,發現穩定后內存占用情況如下圖所示:?
?
比對前后兩圖,不考慮界面UI變化消耗的內存,?
可以看出:內存增加的主體部分來自于Native函數,其它類型的內存變化幾乎可以忽略。?
這是因為該SDK初始化時,最主要的工作是在Native層加載底層的so文件。
接下來,我們在demo里調用SDK的接口,批量掃描樣本,統計內存消耗情況如下圖所示:?
?
從圖中可以看出app消耗的內存飆升到了104M,?
與初始化后的內存相比,其中增加主要是code和native類型的內存。
根據2.3小結的描述,我們知道code類型統計的是app進程需要的資源文件、庫文件等,?
因此這部分內存主要是SDK中引擎加載病毒庫等消耗掉的內存;?
而Native內存的消耗,應該也是由于引擎掃描文件導致的。
按照3.1小結的描述,我們查看了一下批量掃描這段時間內,?
app進程內存分配的情況,如下圖所示:?
?
容易看出,在這段時間內,除去native內存外,整個app分配內存并不大,?
且按照對象占用內存的大小排序,排在前列的都是基本數據類型。?
因此,這段時間內app進程的內存應該是比較正常的。
我們進行GC操作,內存情況如下圖所示:?
?
發現內存幾乎和掃描前一致,按照3.2小結進行dump分析,沒有發現泄露對象。?
因此,可以確認SDK不存在內存泄露的問題(dump信息含有sdk的隱私,這里就不再附圖了)。
五、總結?
至此,我們已經介紹了Android Memory Profiler工具的基本情況,?
并簡單列舉了該工具的使用方式。
個人感覺,不同于LeakCanary,?
Android Memory Profiler更側重于宏觀場景下的內存分析。
總結
以上是生活随笔為你收集整理的Android内存分析工具:Memory Profiler的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android O: View的绘制流程
- 下一篇: Android O: View的绘制流程