1.Memory Profiler
1.1Memory Profiler 的默認(rèn)視圖

- 用于強(qiáng)制執(zhí)行垃圾回收事件的按鈕。
- 用于捕獲堆轉(zhuǎn)儲(chǔ)的按鈕。
注意:只有在連接到搭載 Android 7.1(API 級別 25)或更低版本的設(shè)備時(shí),才會(huì)在堆轉(zhuǎn)儲(chǔ)按鈕右側(cè)顯示用于記錄內(nèi)存分配]的按鈕。 - 一個(gè)下拉菜單,用于指定分析器捕獲內(nèi)存分配的頻率。選擇適當(dāng)?shù)倪x項(xiàng)可幫助您在分析時(shí)提高應(yīng)用性能。
- 用于放大/縮小時(shí)間軸的按鈕。
- 用于跳轉(zhuǎn)到實(shí)時(shí)內(nèi)存數(shù)據(jù)的按鈕。
- 事件時(shí)間軸,顯示活動(dòng)狀態(tài)、用戶輸入事件和屏幕旋轉(zhuǎn)事件。
- 內(nèi)存使用量時(shí)間軸,它包括以下內(nèi)容:
- 一個(gè)堆疊圖表,顯示每個(gè)內(nèi)存類別當(dāng)前使用多少內(nèi)存,如左側(cè)的 y 軸以及頂部的彩色鍵所示。
- 一條虛線,表示分配的對象數(shù),如右側(cè)的 y 軸所示。
- 每個(gè)垃圾回收事件的圖標(biāo)。
不過,如果您使用的是搭載 Android 7.1 或更低版本的設(shè)備,則并非所有分析數(shù)據(jù)在默認(rèn)情況下都可見。如果您看到一條消息,顯示“Advanced profiling is unavailable for the selected process”,您需要啟用高級分析才能看到以下內(nèi)容:
- 事件時(shí)間軸
- 分配的對象數(shù)
- 垃圾回收事件
要啟用高級分析功能,請按以下步驟操作:
- 依次選擇 Run > Edit Configurations。
- 在左側(cè)窗格中選擇您的應(yīng)用模塊。
- 點(diǎn)擊 Profiling 標(biāo)簽,然后勾選 Enable advanced profiling。重新編譯并運(yùn)行您的應(yīng)用。
- 在 Android 8.0 及更高版本上,始終為可調(diào)試應(yīng)用啟用高級分析。
1.2內(nèi)存分析

內(nèi)存計(jì)數(shù)中的類別如下:
- Java:從 Java 或 Kotlin 代碼分配的對象的內(nèi)存。
-
Native:從 C 或 C++ 代碼分配的對象的內(nèi)存。
即使您的應(yīng)用中不使用 C++,您也可能會(huì)看到此處使用的一些原生內(nèi)存,因?yàn)?Android 框架使用原生內(nèi)存代表您處理各種任務(wù),如處理圖像資源和其他圖形時(shí),即使您編寫的代碼采用 Java 或 Kotlin 語言。 - Graphics:圖形緩沖區(qū)隊(duì)列向屏幕顯示像素(包括 GL 表面、GL 紋理等等)所使用的內(nèi)存。(請注意,這是與 CPU 共享的內(nèi)存,不是 GPU 專用內(nèi)存。)
- Stack:您的應(yīng)用中的原生堆棧和 Java 堆棧使用的內(nèi)存。這通常與您的應(yīng)用運(yùn)行多少線程有關(guān)。
- Code:您的應(yīng)用用于處理代碼和資源(如 dex 字節(jié)碼、經(jīng)過優(yōu)化或編譯的 dex 代碼、.so 庫和字體)的內(nèi)存。
- Others:您的應(yīng)用使用的系統(tǒng)不確定如何分類的內(nèi)存。
-
Allocated:您的應(yīng)用分配的 Java/Kotlin 對象數(shù)。此數(shù)字沒有計(jì)入 C 或 C++ 中分配的對象。
如果連接到搭載 Android 7.1 及更低版本的設(shè)備,只有在 Memory Profiler 連接到您運(yùn)行的應(yīng)用時(shí),才開始此分配計(jì)數(shù)。因此,您開始分析之前分配的任何對象都不會(huì)被計(jì)入。不過,Android 8.0 及更高版本附帶一個(gè)設(shè)備內(nèi)置分析工具,該工具可跟蹤所有分配,因此,在 Android 8.0 及更高版本上,此數(shù)字始終表示您的應(yīng)用中待處理的 Java 對象總數(shù)。
1.3內(nèi)存收集結(jié)果

Allocations:堆中的分配數(shù)。
-
Native Size:此對象類型使用的原生內(nèi)存總量(以字節(jié)為單位)。只有在使用 Android 7.0 及更高版本時(shí),才會(huì)看到此列。
您會(huì)在此處看到采用 Java 分配的某些對象的內(nèi)存,因?yàn)?Android 對某些框架類(如 Bitmap)使用原生內(nèi)存。
Shallow Size:此對象類型使用的 Java 內(nèi)存總量(以字節(jié)為單位)。
Retained Size:為此類的所有實(shí)例而保留的內(nèi)存總大?。ㄒ宰止?jié)為單位)。
您可以使用已分配對象列表上方的兩個(gè)菜單來選擇要檢查的堆轉(zhuǎn)儲(chǔ)以及如何組織數(shù)據(jù)。
從左側(cè)的菜單中,選擇要檢查的堆:
- default heap:當(dāng)系統(tǒng)未指定堆時(shí)。
- app heap:您的應(yīng)用在其中分配內(nèi)存的主堆。
- image heap:系統(tǒng)啟動(dòng)映像,包含啟動(dòng)期間預(yù)加載的類。此處的分配保證絕不會(huì)移動(dòng)或消失。
- zygote heap:寫時(shí)復(fù)制堆,其中的應(yīng)用進(jìn)程是從 Android 系統(tǒng)中派生的。
從右側(cè)的菜單中,選擇如何安排分配:
- Arrange by class:根據(jù)類名稱對所有分配進(jìn)行分組。這是默認(rèn)選項(xiàng)。
- Arrange by package:根據(jù)軟件包名稱對所有分配進(jìn)行分組。
- Arrange by callstack:將所有分配分組到其對應(yīng)的調(diào)用堆棧。只有在記錄分配期間捕獲堆轉(zhuǎn)儲(chǔ)時(shí),此選項(xiàng)才有效。即便如此,堆中也很可能有在您開始記錄之前分配的對象,所以會(huì)先顯示這些分配,直接按類名稱列出它們。
默認(rèn)情況下,此列表按 Retained Size 列排序。要按其他列中的值排序,請點(diǎn)擊該列的標(biāo)題。
點(diǎn)擊一個(gè)類名稱可在右側(cè)打開 Instance View 窗口。列出的每個(gè)實(shí)例都包含以下信息:
- Depth:從任意 GC 根到選定實(shí)例的最短跳數(shù)。
- Native Size:原生內(nèi)存中此實(shí)例的大小。只有在使用 Android 7.0 及更高版本時(shí),才會(huì)看到此列。
- Shallow Size:Java 內(nèi)存中此實(shí)例的大小。
- Retained Size:此實(shí)例支配的內(nèi)存大小。
分析簡單的內(nèi)存泄露的時(shí)候,可以通過Activity來回旋轉(zhuǎn)或者Activity進(jìn)入退出來查看對應(yīng)Activity的實(shí)例個(gè)數(shù)。
分析內(nèi)存溢出的時(shí)候,可以依據(jù)Shallow Size等參數(shù)作為參考,查看實(shí)例的個(gè)數(shù)與大小,從而定位到內(nèi)存溢出的具體的問題。
1.4將堆轉(zhuǎn)儲(chǔ)另存為 HPROF 文件
您可以使用 android_sdk/platform-tools/ 目錄中提供的 hprof-conv工具執(zhí)行此操作。運(yùn)行包含兩個(gè)參數(shù)(即原始 HPROF 文件和轉(zhuǎn)換后 HPROF 文件的寫入位置)的 hprof-conv 命令。例如:
hprof-conv heap-original.hprof heap-converted.hprof
hprof文件轉(zhuǎn)換成功后就可以使用其他的內(nèi)存分析工具來分析了,比如MAT等。
2.MAT
MAT是Memory Analyzer的簡稱,它是一款功能強(qiáng)大的Java堆內(nèi)存分析器。可以用于查找內(nèi)存泄露以及查看內(nèi)存消耗情況。MAT是基于Eclipse開發(fā)的,是一款免費(fèi)的性能分析工具。讀者可以在http://www.eclipse.org/mat/下載并使用MAT。
使用步驟:
- 1.首先通過Memory Profiler抓取hprof
- 2.使用命令轉(zhuǎn)成MAT工具識別的文件
hprof-conv heap-original.hprof heap-converted.hprof
- 3.使用Eclipse打開hprof文件
- 4.工具加載成功后,會(huì)生成內(nèi)存泄漏的可疑報(bào)告。然后點(diǎn)擊Open Dominator Tree for entire Heaps,查詢Heap的樹形結(jié)構(gòu)。
image.png - 5.通過open query browser也就是圖片的第二個(gè)紅框進(jìn)行條件查詢。一般情況下是通過Path to Gc Root,去除所有軟弱等引用,得到剩余的一些對象,這些對象基本上就是發(fā)生泄漏問題的對象。
或者可以直接搜索可疑的對象類名稱,查詢對應(yīng)的引用。
image.png
如上圖,我們看下MainActivity2對象有好幾個(gè),同時(shí)處于no GC root狀態(tài),這時(shí)候我們就可以知道MainActivity2對象發(fā)生了泄漏,此時(shí)右擊該對象,選擇Merge ShorTest Path to Gc Roots,得到該對象具體的引用路徑。
image.png
此時(shí)我們就定位到了MainActivity2類的mContext屬性發(fā)生了泄漏。因此我們就可以針對此進(jìn)行代碼層面的優(yōu)化。
以上是這兩個(gè)內(nèi)存分析工具的簡單使用,這些工具還有很多其他的功能,比如線程監(jiān)控等,有興趣的同學(xué)可以深入研究。


