Android 如何做一次内存泄漏大排查
轉(zhuǎn)載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/112335970
本文出自【趙彥軍的博客】
文章目錄
- 前言
- 把內(nèi)存泄漏的地方找出來
- leakcanary 狀態(tài)
- hprof
- 如何獲取 HPROF
- 如何打開 hprof 文件
- Heap Dump
- Heap Dump 是什么?
- Heap Dump里面有什么?
- 如何做一次 `Heap Dump` ?
- 如何代碼觸發(fā) Heap Dump
- 如何手動觸發(fā) GC
- Android Profiler指標
- Heap Dump指標分析
- 對象跟蹤策略
- 記錄某一段時間的內(nèi)存分配情況
- Memory Analyzer(MAT)
- 祝福
前言
眼瞅著還有一個月就過年,項目也沒有那么忙了,技術(shù)老大要求做一做性能優(yōu)化方面的工作。
而我的任務(wù)就是把項目中的內(nèi)存泄漏擼一遍,然后安排對應(yīng)的人處理。
說到內(nèi)存泄漏,我也算是老手了,其實在 2016 年我就寫個內(nèi)存泄漏方面的文章:
Android 如何有效的解決內(nèi)存泄漏的問題
把內(nèi)存泄漏的地方找出來
說干就干,首要的任務(wù)就是把內(nèi)存泄漏的代碼揪出來,我選擇 leakcanary
github 地址:https://github.com/square/leakcanary/
關(guān)于 leakcanary 的介紹,集成步驟,我就不在這里展開講述了,網(wǎng)上有很多文章,你們自己搜索一下。
我只啰嗦一點:
- 1、 leakcanary 2.0之后的版本集成不需要初始化
集成完成后,把項目跑起來,過一會就會報出來很多內(nèi)存泄漏的日志。
在 Android Studio logcat 過濾 LeakCanary 就會看到如下:
同時在手機桌面也會有一個 小鳥圖標,點擊會看到可視化頁面
這里就可以看到內(nèi)存泄漏的鏈條,分析的方式是 從下往上 的順序,比如針對本圖:
首先 MainActivity 實例發(fā)生內(nèi)存泄漏 --> 再往上可以看到 MainActivity 泄漏 原因是 MainActivity 里的一個 Lambda 表達式引起的 --> … --> 再往上 看到 MutableLiveData 引起的 --> … --> …
看到這個分析鏈條,我們就很清楚了,大概率是 MutableLiveData 對象引起的,再結(jié)合實際的項目代碼,最后發(fā)現(xiàn)果然是因為 MainActivity 里的 MutableLiveData 對象沒有釋放。
特別要注意的是:
縱然 leakcanary 工具很牛逼,但是要想清晰的定位,然后修復(fù)內(nèi)存泄漏,還是要結(jié)合實際的項目代碼的。
到這里我們基本就完成了 把內(nèi)存泄漏揪出來 的問題。
leakcanary 狀態(tài)
在上面一部分,我貼了兩個圖,圖中的有很清晰的對象引用鏈條。leakcanary 對每個對象都標明了泄漏的狀態(tài)。
- Leaking: YES 確定已經(jīng)泄漏
- Leaking: UNKNOWN 不確定是否泄漏
- Leaking: NO 沒有泄漏
我們在分析 對象引用鏈條的時候,要特別注意 UNKNOWN 狀態(tài),這個狀態(tài)即有可能是泄漏了,也有可能是沒有泄漏,這就需要我們程序要認真的分析項目代碼,然后給出結(jié)論
hprof
leakcanary 在運行的時候,發(fā)現(xiàn)內(nèi)存泄漏了,會把 Java堆快照轉(zhuǎn)儲到Android HPROF文件中,方便開發(fā)者分析。
如何獲取 HPROF
方式一:通過 logcat 獲取
在 Android Studio 的 logcat 中會輸出 hprof 文件地址:
我們拿到 hprof 文件鏈接后,就可以通過 adb pull 命令導(dǎo)到電腦桌面
adb pull /storage/emulated/0/Download/leakcanary-com.cootek.crazyreader/2021-01-08_11-21-08_472.hprof ~/DeskTop
方式二:通過客戶端可視化頁面
點擊 Share Heap Dump file 可以通過分享出去。
正文到這里其實也就結(jié)束了。在做內(nèi)存泄漏排查的時候用到了 AndroidStudio Profiler 工具,里面有很多新的概念和內(nèi)存指標,下面的內(nèi)容就是在探究 Profiler 工具如何使用以及各種內(nèi)存指標所代表的含義
如何打開 hprof 文件
方式一:Android studio Profiler 功能打開
這個工具顯示了如下信息:
| Class name | 類名 |
| Total Count | 該類的實例總數(shù) |
| Heap Count | 所選擇的堆中該類的實例的數(shù)量 |
| Sizeof | 單個實例所占空間大小(如果每個實例所占空間大小不一樣則顯示0) |
| Shallow Size | 堆里所有實例大小總和(Heap Count * Sizeof) |
| Retained Size | 該類所有實例所支配的內(nèi)存大小 |
| Instance | 具體的實例 |
| Reference Tree | 所選實例的引用,以及指向該引用的引用。 |
| Depth | GC根節(jié)點到所選實例的最短路徑的深度 |
| Shallow Size | 所選實例的大小 |
| Dominating Size | 所選實例所支配的內(nèi)存大小 |
用HPROF分析工具,可以檢測到泄漏的 Activity
通過這個可以看到 本次分析有 3 出泄漏的地方,點擊 第一個 ReaderActivity
可以看到詳細的泄漏實例,Depth 為 13 ,代表 GC根節(jié)點到所選ReaderActivity實例的最短路徑的深度是 13 。
Heap Dump
Heap Dump 是什么?
Heap Dump 也叫堆轉(zhuǎn)儲文件,是一個 Java 進程在某個時間點上的內(nèi)存快照。Heap Dump 是有著多種類型的。不過總體上 heap dump 在觸發(fā)快照的時候都保存了 java對象和類的信息。通常在寫heap dump文件前會觸發(fā)一次FullGC,所以heap dump文件中保存的是FullGC后留下的對象信息。
簡單說就是:
heap dump文件是一個二進制文件,它保存了某一時刻JVM堆中對象使用情況。HeapDump文件是指定時刻的Java堆棧的快照,是一種鏡像文件。
Heap Dump里面有什么?
一般在 Heap Dump 文件中可以獲取到(這仍然取決于heap dump文件的類型)如下信息:
- 對象信息:類、成員變量、引用值;
- 類信息:類加載器、名稱、超類、靜態(tài)成員;
- Garbage Collections Roots:JVM可達的對象;
- 線程棧以及本地變量:獲取快照時的線程棧信息,以及局部變量的詳細信息
也就是說我們可以對上面這些內(nèi)容進行分析。通常可以基于 Heap Dump 分析如下類型的問題:
- 找出內(nèi)存泄漏的原因;
- 找出重復(fù)引用的jar或類;
- 分析集合的使用;
- 分析類加載器。
總而言之我們對 Heap Dump 的分析就是對應(yīng)用的內(nèi)存使用進行分析,從而更加合理地使用內(nèi)存。
如何做一次 Heap Dump ?
在前面講到的,hprof 文件都是 Leakcanary 工具幫我們做的,那我們自己想要自己做一次 Heap Dump ,生成 hprof 文件又該怎么做呢?
其實 AndroidStudio 有現(xiàn)成的工具,只要動動手機就行了。
AndroidStudio --> Profiler --> 點擊 + 號 --> 選擇設(shè)備 --> 選擇進程 --> 點擊 MEMORY --> 點擊 向下的箭頭
由于生成的 hprof 文件比較大,所以解析出來比較慢,要耐心等待。
至此,我們就完成手動 Heap Dump 操作,并且生成 hprof 文件 。我們也可以點擊保存按鈕,把 hprof 文件保存到桌面,或者發(fā)給其他人。
如何代碼觸發(fā) Heap Dump
代碼其實很簡單:
try {//指定Hprof文件的名字var path: String =externalCacheDir?.absolutePath + File.separator + System.currentTimeMillis() + ".hprof"Debug.dumpHprofData(path) } catch (e: Exception) {}生成的文件在 Android/data/app包名/cache/ 目錄下:
如何手動觸發(fā) GC
AndroidStudio --> Profiler --> 點擊 + 號 --> 選擇設(shè)備 --> 選擇進程 --> 點擊 MEMORY --> 點擊 像垃圾桶 的圖標
其實最快速的是點擊 右鍵
Android Profiler指標
官方文檔:https://developer.android.com/studio/profile/memory-profiler
內(nèi)存計數(shù)中的類別如下:
-
Java:從 Java 或 Kotlin 代碼分配的對象的內(nèi)存。
-
Native:從 C 或 C++ 代碼分配的對象的內(nèi)存。
即使您的應(yīng)用中不使用 C++,您也可能會看到此處使用了一些原生內(nèi)存,因為即使您編寫的代碼采用 Java 或 Kotlin 語言,Android 框架仍使用原生內(nèi)存代表您處理各種任務(wù),如處理圖像資源和其他圖形。
-
Graphics:圖形緩沖區(qū)隊列為向屏幕顯示像素(包括 GL 表面、GL紋理等等)所使用的內(nèi)存。(請注意,這是與 CPU 共享的內(nèi)存,不是 GPU專用內(nèi)存。)
-
Stack:您的應(yīng)用中的原生堆棧和 Java 堆棧使用的內(nèi)存。這通常與您的應(yīng)用運行多少線程有關(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ù)字沒有計入 C 或 C++ 中分配的對象。
如果連接到搭載 Android 7.1 及更低版本的設(shè)備,只有在內(nèi)存性能分析器連接到您運行的應(yīng)用時,才開始此分配計數(shù)。因此,您開始分析之前分配的任何對象都不會被計入。但是,Android 8.0 及更高版本附帶一個設(shè)備內(nèi)置性能剖析工具,該工具可跟蹤所有分配,因此,在 Android 8.0 及更高版本上,此數(shù)字始終表示您的應(yīng)用中待處理的 Java 對象總數(shù)。
Heap Dump指標分析
只有看懂了每個指標,才能更好的分析內(nèi)存,下面我們分析一下 heap Dump 指標
對象跟蹤策略
為了在分析時提高應(yīng)用性能,內(nèi)存性能分析器在默認情況下會定期對內(nèi)存分配進行采樣。在運行 API 級別 26 或更高級別的設(shè)備上進行測試時,您可以使用 Allocation Tracking 下拉菜單更改此行為。可用選項如下:
- Full:捕獲內(nèi)存中的所有對象分配。這是 Android Studio 3.2 及更低版本中的默認行為。如果您有一個分配了大量對象的應(yīng)用,可能會在分析時觀察到應(yīng)用的運行速度明顯減慢。
- Sampled:定期對內(nèi)存中的對象分配進行采樣。這是默認選項,在分析時對應(yīng)用性能的影響較小。在短時間內(nèi)分配大量對象的應(yīng)用仍可能會表現(xiàn)出明顯的速度減慢。
- Off/None:停止跟蹤應(yīng)用的內(nèi)存分配。
記錄某一段時間的內(nèi)存分配情況
Heap Dump 是一個很好用的工具,能夠分析內(nèi)存中所有的對象,但是也是有弊端的,Heap Dump 是全量分析,如果你想分析某一段時間內(nèi)的內(nèi)存增量分配情況,該怎么做呢?點擊 Record 按鈕.
下面用一個 gif 看看
Memory Analyzer(MAT)
Memory Analyzer 工具,簡稱:MAT 。
MAT 是 Eclipse 下的一個軟件,專門用來分析 Java內(nèi)存堆。
官方下載地址:https://www.eclipse.org/mat/
安裝完成后,圖標如下
不得不說,這個工具長得很丑。
MAT可以打開 .hprof , 但是從 AndroidStudio 里面的導(dǎo)出的 .hprof 文件,MAT 是不支持查看的,所以需要轉(zhuǎn)化一下,Android SDK 自帶了轉(zhuǎn)化工具。
您可以使用 android_sdk/platform-tools/ 目錄中提供的 hprof-conv工具執(zhí)行此操作。運行包含兩個參數(shù)(即原始 HPROF 文件和轉(zhuǎn)換后 HPROF 文件的寫入位置)的hprof-conv 命令。例如:
hprof-conv heap-original.hprof heap-converted.hprof
經(jīng)過轉(zhuǎn)化過的 .hprof 文件,MAT 就可以打開了。
祝福
快過年了,祝大家 2021 事事順心,萬事大吉。新年快樂鴨 !!
總結(jié)
以上是生活随笔為你收集整理的Android 如何做一次内存泄漏大排查的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 跨进程通信大总结
- 下一篇: Android 打造异常崩溃捕获工具