一视搏、前言
我們知道碉就,Android系統(tǒng)檢測到app有不再使用對象時回梧,就會進行內(nèi)存回收相關(guān)的工作废岂。
盡管Android檢測無用對象、回收內(nèi)存的方法在不斷改進狱意,
但在目前所有的Android版本中湖苞,進行上述工作時,系統(tǒng)仍需要短暫地停止app的運行详囤。
在大多數(shù)情況下财骨,系統(tǒng)進行內(nèi)存回收的行為是無法被用戶察覺到的。
然而纬纪,如果應(yīng)用分配內(nèi)存的速度大于系統(tǒng)回收的速度蚓再,
那么app進程的正常運行可能就回受到影響。
畢竟包各,系統(tǒng)必須回收到足夠的供app需要的內(nèi)存,才會恢復(fù)處于暫停狀態(tài)的app靶庙。
在這種情況下问畅,app就可能出現(xiàn)掉幀、卡頓等現(xiàn)象。
在更嚴(yán)重的情況下护姆,如果出現(xiàn)了內(nèi)存泄露的問題矾端,那么系統(tǒng)中就可能堆積無法釋放的內(nèi)存,
使得系統(tǒng)必須更加頻繁地進行內(nèi)存回收卵皂,從而降低系統(tǒng)的性能秩铆。
甚至在極端條件下,系統(tǒng)不得不殺死部分正在后臺運行的app進程灯变。
于是用戶將后臺應(yīng)用移到前臺時殴玛,卻發(fā)現(xiàn)應(yīng)用無故重啟,這顯然帶來了較差的用戶體驗添祸。
由此可見滚粟,內(nèi)存對于app而言,是極其關(guān)鍵的性能指標(biāo)刃泌。
目前凡壤,分析app內(nèi)存的工具有很多,
本文主要記錄一下Android Studio內(nèi)置的內(nèi)存分析工具Memory Profiler耙替。
二亚侠、基本介紹
Memory Profiler是Android Profiler的一個組件, 用于幫助分析內(nèi)存泄露和內(nèi)存抖動的問題俗扇。
當(dāng)PC連接Android L以上的設(shè)備時硝烂,該工具才能夠正常使用。
Memory Profiler的功能包括:
展示應(yīng)用內(nèi)存使用情況的實時圖像狐援、抓取內(nèi)存的dump信息钢坦、強制垃圾回收及追蹤內(nèi)存分配。
2.1 開啟步驟
打開Memory Profiler的步驟為:
1啥酱、 依次點擊Android Studio的View → Tool Windows → Android Profiler爹凹,
或直接點擊工具欄Android Profiler對應(yīng)的圖標(biāo);
2镶殷、 PC連接Android終端后禾酱,在Android Profiler對應(yīng)的區(qū)域選擇接的設(shè)備和需要監(jiān)控的進程:
3、 點擊Android Profiler界面中MEMORY區(qū)域的任意位置绘趋,即可開啟Memory Profiler颤陶,如下圖所示:
需要注意的是,如果PC連接Android 7.1以下的設(shè)備時陷遮,有些關(guān)鍵數(shù)據(jù)可能無法被Android Profiler統(tǒng)計滓走,
此時Android Profiler會顯示如下信息:
這時我們需要依次點擊Android Studio的Run → Edit Configurations → Profiling 按鍵,選中app后點擊Enabled advanced profiling帽馋,
如下圖所示:
為了支持該功能搅方,要求app對應(yīng)的gradle版本必須在2.4以上比吭。
2.2 界面介紹
打開Memory Profiler后,主界面如下所示(為了方便姨涡,這里直接盜取Android技術(shù)文檔中的圖):
其中:
標(biāo)注1對應(yīng)的按鍵用于強制內(nèi)存回收衩藤。
標(biāo)注2對應(yīng)的按鍵用于抓取進程內(nèi)存的dump信息。
標(biāo)注3對應(yīng)的按鍵用于記錄內(nèi)存的分配信息(連接Android 7.1及以下才會有此按鍵)涛漂。
初次點擊時赏表,對應(yīng)統(tǒng)計的開始時間點;再次點擊時匈仗,對應(yīng)統(tǒng)計的結(jié)束時間點瓢剿。
進程在兩個時間點之間的內(nèi)存分配信息,將被Memory Profiler記錄和分析锚沸。
標(biāo)注4對應(yīng)的區(qū)域用于縮放時間軸跋选。
標(biāo)注5對應(yīng)的按鍵用于顯示實時的內(nèi)存數(shù)據(jù)。
標(biāo)注6對應(yīng)的區(qū)域用于記錄事件發(fā)生的時間點及大致持續(xù)的時間(例如activity狀態(tài)改變哗蜈、用戶操作界面等事件)前标。
標(biāo)注7對應(yīng)的區(qū)域用于顯示內(nèi)存使用情況對應(yīng)的時間軸(與標(biāo)注6結(jié)合,就可以看出各事件帶來的內(nèi)存變化情況)距潘。
需要說明的是炼列,標(biāo)注7對應(yīng)區(qū)域顯示的內(nèi)容包括:
不同類型內(nèi)存占用情況對應(yīng)的圖像;
分配對象數(shù)量對應(yīng)的短畫線音比;
內(nèi)存回收事件發(fā)生的時機俭尖。
2.3 統(tǒng)計的數(shù)據(jù)類型及含義
Memory Profiler主要根據(jù)Android系統(tǒng)提供的信息,
統(tǒng)計app獨自占用內(nèi)存洞翩,即不統(tǒng)計app與系統(tǒng)或其它app共有的內(nèi)存稽犁。
Memory Profiler統(tǒng)計內(nèi)存的種類如下圖所示:
如上圖所示,其中:
Java表示Java代碼或Kotlin代碼分配的內(nèi)存骚亿;
Native表示C或C++代碼分配的內(nèi)存(即使App沒有native層已亥,調(diào)用framework代碼時,也有可能觸發(fā)分配native內(nèi)存)来屠;
Graphics表示圖像相關(guān)緩存隊列占用的內(nèi)存虑椎;
Stack表示native和java占用的棧內(nèi)存;
Code表示代碼俱笛、資源文件捆姜、庫文件等占用的內(nèi)存;
Others表示無法明確分類的內(nèi)存迎膜;
Allocated表示Java或Kotlin分配對象的數(shù)量(Android8.0以下時泥技,僅統(tǒng)計Memory Profiler啟動后,進程再分配的對象數(shù)量磕仅;
8.0以上時零抬,由于系統(tǒng)內(nèi)置了統(tǒng)計工具镊讼,Memory Profiler可以得到整個app啟動后分配對象的數(shù)量)宽涌。
三平夜、基本用法
對Memory Profiler有了基本的了解后,我們來看看它的基本用法卸亮。
3.1 查看內(nèi)存分配情況
Memory Profiler可以查看兩個時間點之間的內(nèi)存分配情況忽妒,包括:
對象的類型、占用內(nèi)存的大小兼贸、棧信息等段直。
連接8.0以上的設(shè)備時,Memory Profiler還可以顯示對象被回收的時間溶诞。
PC連接8.0以上的設(shè)備時鸯檬,在內(nèi)存統(tǒng)計的時間線上,直接點擊和拖動就可以選擇觀察區(qū)域螺垢;
連接低版本的設(shè)備時喧务,則需要點擊Record Memory allocations按鍵(2.2小結(jié)介紹的標(biāo)注3)選擇觀察區(qū)域。
選定觀察區(qū)域后枉圃, Memory Profiler就可以統(tǒng)計這段時間內(nèi)app分配內(nèi)存的情況:
從圖中可以看出功茴,Memory Profiler可以顯示分配對象的類名;
點擊類后孽亲,會在Instance View顯示具體的對象坎穿;
點擊具體對象后,會在Call back區(qū)域顯示調(diào)用棧返劲。
點擊調(diào)用棧信息后玲昧,就會跳轉(zhuǎn)到具體的代碼。
3.2 查看內(nèi)存占用情況
點擊2.2小結(jié)介紹的標(biāo)注2篮绿,即可抓取點擊后一段時間內(nèi)app占用內(nèi)存的dump信息孵延。
通過dump信息,我們可以看到app當(dāng)前仍存在于內(nèi)存中的對象搔耕。
結(jié)合代碼隙袁,我們可以分析是否有本應(yīng)被析構(gòu)卻仍存活的泄露對象。
與統(tǒng)計內(nèi)存分配信息一樣弃榨,內(nèi)存占用信息同樣會顯示對象的類型菩收、數(shù)量、占用內(nèi)存的大小鲸睛、引用關(guān)系等娜饵。
如下圖所示:
圖中Alloc Count表示堆中分配對象的數(shù)量;
Shallow Size表示對象使用Java內(nèi)存的大小官辈,單位為byte箱舞;
Retained Size表示對象占用的實際內(nèi)存大小遍坟,大于等于Shallow Size;
7.0及以上版本的設(shè)備晴股,還會顯示對象占用的Native Size愿伴。
點擊具體的對象時,也會顯示Instance View电湘。
此時隔节,Instance View顯示的信息變多了,包括:
Depth表示當(dāng)前對象到任一GC root的最短跳數(shù)寂呛;
Shallow Size枝缔、Retained Size的含義與前文一致剧蹂。
同樣少态,7.0及以上版本的設(shè)備帖鸦,還會顯示對象占用的Native Size。
從圖中可以看出劫拢,Instance View不會顯示棧信息肉津。
如果想獲得棧信息的話,必須先點擊Record Memory allocations按鍵尚镰。
四阀圾、使用示例
利用Memory Profiler,我分析了一下某反病毒引擎SDK的內(nèi)存占用情況狗唉。
隨便寫了個demo初烘,繼承SDK后啟動app,內(nèi)存占用情況如下圖所示:
然后分俯,通過操作UI初始化SDK肾筐,發(fā)現(xiàn)穩(wěn)定后內(nèi)存占用情況如下圖所示:
比對前后兩圖,不考慮界面UI變化消耗的內(nèi)存缸剪,
可以看出:內(nèi)存增加的主體部分來自于Native函數(shù)吗铐,其它類型的內(nèi)存變化幾乎可以忽略。
這是因為該SDK初始化時杏节,最主要的工作是在Native層加載底層的so文件唬渗。
接下來,我們在demo里調(diào)用SDK的接口奋渔,批量掃描樣本镊逝,統(tǒng)計內(nèi)存消耗情況如下圖所示:
從圖中可以看出app消耗的內(nèi)存飆升到了104M,
與初始化后的內(nèi)存相比嫉鲸,其中增加主要是code和native類型的內(nèi)存撑蒜。
根據(jù)2.3小結(jié)的描述,我們知道code類型統(tǒng)計的是app進程需要的資源文件、庫文件等座菠,
因此這部分內(nèi)存主要是SDK中引擎加載病毒庫等消耗掉的內(nèi)存狸眼;
而Native內(nèi)存的消耗,應(yīng)該也是由于引擎掃描文件導(dǎo)致的浴滴。
按照3.1小結(jié)的描述拓萌,我們查看了一下批量掃描這段時間內(nèi),
app進程內(nèi)存分配的情況巡莹,如下圖所示:
容易看出司志,在這段時間內(nèi),除去native內(nèi)存外降宅,整個app分配內(nèi)存并不大,
且按照對象占用內(nèi)存的大小排序囚霸,排在前列的都是基本數(shù)據(jù)類型腰根。
因此,這段時間內(nèi)app進程的內(nèi)存應(yīng)該是比較正常的拓型。
我們進行GC操作额嘿,內(nèi)存情況如下圖所示:
發(fā)現(xiàn)內(nèi)存幾乎和掃描前一致,按照3.2小結(jié)進行dump分析劣挫,沒有發(fā)現(xiàn)泄露對象册养。
因此,可以確認(rèn)SDK不存在內(nèi)存泄露的問題(dump信息含有sdk的隱私压固,這里就不再附圖了)球拦。
五、總結(jié)
至此帐我,我們已經(jīng)介紹了Android Memory Profiler工具的基本情況坎炼,
并簡單列舉了該工具的使用方式。
個人感覺拦键,不同于LeakCanary谣光,
Android Memory Profiler更側(cè)重于宏觀場景下的內(nèi)存分析。
————————————————
版權(quán)聲明:本文為CSDN博主「ZhangJianIsAStark」的原創(chuàng)文章芬为,遵循 CC 4.0 BY 版權(quán)協(xié)議萄金,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/gaugamela/article/details/79027538