天天看點

Android 如何做一次記憶體洩漏大排查

轉載請标明出處: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、

    leakcanary

    2.0之後的版本內建不需要初始化

內建完成後,把項目跑起來,過一會就會報出來很多記憶體洩漏的日志。

在 Android Studio

logcat

過濾

LeakCanary

就會看到如下:

Android 如何做一次記憶體洩漏大排查

同時在手機桌面也會有一個 小鳥圖示,點選會看到可視化頁面

Android 如何做一次記憶體洩漏大排查

這裡就可以看到記憶體洩漏的鍊條,分析的方式是

從下往上

的順序,比如針對本圖:

首先 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 檔案位址:

Android 如何做一次記憶體洩漏大排查

我們拿到 hprof 檔案連結後,就可以通過

adb pull

指令導到電腦桌面

adb pull /storage/emulated/0/Download/leakcanary-com.cootek.crazyreader/2021-01-08_11-21-08_472.hprof ~/DeskTop

方式二:通過用戶端可視化頁面

Android 如何做一次記憶體洩漏大排查

點選

Share Heap Dump file

可以通過分享出去。

正文到這裡其實也就結束了。在做記憶體洩漏排查的時候用到了 AndroidStudio Profiler 工具,裡面有很多新的概念和記憶體名額,下面的内容就是在探究 Profiler 工具如何使用以及各種記憶體名額所代表的含義

如何打開 hprof 檔案

方式一:

Android studio

Profiler 功能打開

Android 如何做一次記憶體洩漏大排查

這個工具顯示了如下資訊:

名稱 描述
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

Android 如何做一次記憶體洩漏大排查

通過這個可以看到 本次分析有 3 出洩漏的地方,點選 第一個 ReaderActivity

Android 如何做一次記憶體洩漏大排查

可以看到詳細的洩漏執行個體,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

在前面講到的,

hprof

檔案都是

Leakcanary

工具幫我們做的,那我們自己想要自己做一次

Heap Dump

,生成 hprof 檔案又該怎麼做呢?

其實 AndroidStudio 有現成的工具,隻要動動手機就行了。

AndroidStudio

-->

Profiler

-->

點選 + 号

-->

選擇裝置

-->

選擇程序

-->

點選 MEMORY

-->

點選 向下的箭頭

Android 如何做一次記憶體洩漏大排查

由于生成的 hprof 檔案比較大,是以解析出來比較慢,要耐心等待。

Android 如何做一次記憶體洩漏大排查

至此,我們就完成手動

Heap Dump

操作,并且生成

hprof

檔案 。我們也可以點選儲存按鈕,把

hprof

檔案儲存到桌面,或者發給其他人。

Android 如何做一次記憶體洩漏大排查

如何代碼觸發 Heap Dump

代碼其實很簡單:

try {
     //指定Hprof檔案的名字
     var path: String =
                externalCacheDir?.absolutePath + File.separator + System.currentTimeMillis() + ".hprof"
     Debug.dumpHprofData(path)
} catch (e: Exception) {

}
           

生成的檔案在

Android/data/app包名/cache/

目錄下:

Android 如何做一次記憶體洩漏大排查

如何手動觸發 GC

AndroidStudio

-->

Profiler

-->

點選 + 号

-->

選擇裝置

-->

選擇程序

-->

點選 MEMORY

-->

點選 像垃圾桶 的圖示

Android 如何做一次記憶體洩漏大排查

其實最快速的是點選 右鍵

Android 如何做一次記憶體洩漏大排查

Android Profiler名額

官方文檔:https://developer.android.com/studio/profile/memory-profiler

Android 如何做一次記憶體洩漏大排查

記憶體計數中的類别如下:

  • Java:從 Java 或 Kotlin 代碼配置設定的對象的記憶體。
  • Native:從 C 或 C++ 代碼配置設定的對象的記憶體。

    即使您的應用中不使用 C++,您也可能會看到此處使用了一些原生記憶體,因為即使您編寫的代碼采用 Java 或 Kotlin 語言,Android 架構仍使用原生記憶體代表您處理各種任務,如處理圖像資源和其他圖形。

  • Graphics:圖形緩沖區隊列為向螢幕顯示像素(包括

    GL

    表面、

    GL

    紋理等等)所使用的記憶體。(請注意,這是與 CPU 共享的記憶體,不是

    GPU

    專用記憶體。)
  • Stack:您的應用中的原生堆棧和 Java 堆棧使用的記憶體。這通常與您的應用運作多少線程有關。
  • Code:您的應用用于處理代碼和資源(如

    dex

    位元組碼、經過優化或編譯的

    dex

    代碼、

    .so

    庫和字型)的記憶體。
  • Others:您的應用使用的系統不确定如何分類的記憶體。
  • Allocated:您的應用配置設定的

    Java/Kotlin

    對象數。此數字沒有計入

    C

    C++

    中配置設定的對象。

    如果連接配接到搭載

    Android 7.1

    及更低版本的裝置,隻有在記憶體性能分析器連接配接到您運作的應用時,才開始此配置設定計數。是以,您開始分析之前配置設定的任何對象都不會被計入。但是,

    Android 8.0

    及更高版本附帶一個裝置内置性能剖析工具,該工具可跟蹤所有配置設定,是以,在

    Android 8.0

    及更高版本上,此數字始終表示您的應用中待處理的 Java 對象總數。
Android 如何做一次記憶體洩漏大排查

Heap Dump名額分析

隻有看懂了每個名額,才能更好的分析記憶體,下面我們分析一下

heap Dump

名額

Android 如何做一次記憶體洩漏大排查

對象跟蹤政策

為了在分析時提高應用性能,記憶體性能分析器在預設情況下會定期對記憶體配置設定進行采樣。在運作 API 級别 26 或更進階别的裝置上進行測試時,您可以使用

Allocation Tracking

下拉菜單更改此行為。可用選項如下:

  • Full:捕獲記憶體中的所有對象配置設定。這是 Android Studio 3.2 及更低版本中的預設行為。如果您有一個配置設定了大量對象的應用,可能會在分析時觀察到應用的運作速度明顯減慢。
  • Sampled:定期對記憶體中的對象配置設定進行采樣。這是預設選項,在分析時對應用性能的影響較小。在短時間内配置設定大量對象的應用仍可能會表現出明顯的速度減慢。
  • Off/None:停止跟蹤應用的記憶體配置設定。
    Android 如何做一次記憶體洩漏大排查

記錄某一段時間的記憶體配置設定情況

Heap Dump

是一個很好用的工具,能夠分析記憶體中所有的對象,但是也是有弊端的,

Heap Dump

是全量分析,如果你想分析某一段時間内的記憶體增量配置設定情況,該怎麼做呢?點選

Record

按鈕.

Android 如何做一次記憶體洩漏大排查

下面用一個 gif 看看

Android 如何做一次記憶體洩漏大排查

Memory Analyzer(MAT)

Memory Analyzer

工具,簡稱:

MAT

MAT

是 Eclipse 下的一個軟體,專門用來分析 Java記憶體堆。

官方下載下傳位址:https://www.eclipse.org/mat/

安裝完成後,圖示如下

Android 如何做一次記憶體洩漏大排查

不得不說,這個工具長得很醜。

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

就可以打開了。

Android 如何做一次記憶體洩漏大排查

祝福

快過年了,祝大家 2021 事事順心,萬事大吉。新年快樂鴨 !!