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