天天看點

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

前言

記憶體洩露,這個耳熟能詳的一個bug,每次我在項目中遇到這種問題都非常的頭疼,不像普通的異常奔潰比如JE什麼的,隻要通過Log中的異常堆棧就可以輕松的定位出問題點。那麼到底什麼是記憶體洩漏呢?簡單粗俗的講,就是該被釋放的對象沒有釋放,一直被某個或某些執行個體所持有卻不再被使用導緻 GC 不能回收。導緻這種情況原因是生命周期長的對象持有了生命周期短的對象導緻的生命周期短的對象無法被回收。要想定位出記憶體洩露問題,我們可以通過目前主流的MAT,Android Profiler,LeakCanary等工具來定位問題。

一、MAT簡介

MAT是專門用于分析記憶體的工具,在Eclipse中隻要安裝相應的插件即可。用于解析反應目前裝置記憶體映像的hprof檔案,可以很直覺的開出目前抓到的記憶體情況。一般該檔案包含如下内容:

所有的對象資訊,包括對象執行個體、成員變量、存儲于棧中的基本類型值和存儲于堆中的其他對象的引用值。

所有的類資訊,包括classloader、類名稱、父類、靜态變量等

GCRoot到所有的這些對象的引用路徑

線程資訊,包括線程的調用棧及此線程的線程局部變量(TLS)

二、Android Profiler簡介

Android Studio 3.0 采用全新的Android Profiler視窗取代 Android Monitor 工具。 這些全新的分析工具能夠提供關于應用 CPU、記憶體和網絡 Activity 的實時資料。 您可以執行基于樣本的函數跟蹤來記錄代碼執行時間、采集堆轉儲資料、檢視記憶體配置設定,以及檢視網絡傳輸檔案的詳情。

三、如何抓取hprof檔案

hprof檔案有三種方式來擷取:1、DDMS抓取。2、adb指令行抓取,3、Android Studio抓取。有時抓取到的hprof格式并不是标準的可以使用如下指令轉換

hprof-conv

1、DDMS抓取:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

如上圖所示可以快速抓取到SystmeUI目前記憶體情況的hprof檔案。

2、adb指令行抓取(如果沒有權限開啟可以嘗試adb shell setprop ro.debuggable 1方式來開啟io權限):

1、adb shell am dumpheap (必須要指定out目錄)

2、adb shell pull

通過以上兩個指令即可抓取到hprof檔案

3、Android Studio抓取(基于Android 3.0以上):

首先打開Android Profier視窗我們可以看到如下的内容:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

第一部分用于展示CPU的使用狀态,第二部分用于顯示記憶體使用情況,第三部分展示網絡狀态。今天我們的重點是第二部分的記憶體(MEMORY)。點選MEMORY進入專門的記憶體展示界面,如圖:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

該界面可以實時的展示目前程序記憶體的使用情況,包含了JAVA,NATIVE等記憶體。并通過不同的顔色來區分,可以非常友善的檢視分析問題。如果知道記憶體洩露的必現路徑,可以在該界面清晰的看到記憶體的增長。

然後通過如下方法就可以dump出hprof檔案:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

通過Android Profier生成的hprof檔案也不是标準的,AS提供了便捷的轉換方式:AS提供了便捷的轉換方式:Memory Monitor生成的hpof檔案都會顯示在AS左側的Captures标簽中,在Captures标簽中選擇要轉換的hpof檔案,并點選滑鼠右鍵,在彈出的菜單中選擇Export to standard.hprof選項,即可導出标準的hpof檔案,如下圖所示。

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

Tips : 如果有的同學找不到Android Profiler視窗可以通過如下方式打開:View > Tool windows > Android Profile

四、如何通過MAT分析誰洩露(本文主要講常用的分析方法)

通過MAT打開hprof檔案,選擇Leak Suspects Report選項,就會生産分析報告。主要分為兩個部分:1、Overview。2、Leak Suspects(洩漏猜想)。如下如所示:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

該分析報告可以基本看到記憶體的使用情況,以及一些洩漏猜想(一般定位不出問題來)。主要是通過檢視Histogram 或者 Dominator Tree來分析問題。

Histogram

首先我們先說說Histogram。Histogram主要通過類的角度分析,注重的是量的分析。通過點選工具欄上

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image按鈕選擇柱狀圖方式分析。内容如圖:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

主要有四個方面的内容。1、Class Name。2、Objects(該類的個數)。3、ShallowHeap(該類本身所占用的大小)。4、Retained Heap。

Retained Heap:一個對象的Retained Set所包含對象所占記憶體的總大小。換句話說,Retained Heap就是目前對象被GC後,從Heap上總共能釋放掉的記憶體。(Retained Set指的是這個對象本身和他持有引用的對象以及這些引用對象的Retained Set所占記憶體大小的總和)官方的圖解如下所示:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

從圖中可以看出E的Retained Set為E和G。C的Retained Set為C、D、E、F、G、H。

MAT所定義的支配樹就是從上圖的引用樹演化而來。在引用樹當中,一條到Y的路徑必然會經過X,這就是X支配Y。X直接支配Y則指的是在所有支配Y的對象中,X是Y最近的一個對象。支配樹就是反映的這種直接支配關系,在支配樹中,父節點直接支配子節點。下圖就是官方提供的一個從引用樹到支配樹的轉換示意圖。

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

C直接支配D、E,是以C是D、E的父節點,這一點根據上面的闡述很容易得出結論。C直接支配H,這可能會有些疑問,能到達H的主要有兩條路徑,而這兩條路徑FD和GE都不是必須要經過的節點,隻有C滿足了這一點,是以C直接支配H,C就是H的父節點。通過支配樹,我們就可以很容易的分析一個對象的Retained Set,比如E被回收,則會釋放E、G的記憶體,而不會釋放H的記憶體,因為F可能還引用着H,隻有C被回收,H的記憶體才會被釋放。

假如說我們要找出Activity的情況,我們可以在Regex中輸入XXActivity過濾條件來搜尋。Regex支援正規表達式

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

RecentActivity以及其他各種内部類對象(Activity後面的美元符号代表的是内部類)有3個,可以斷定Activity出現了洩漏。具體檢視為啥導緻了記憶體洩漏,可以通過右擊選擇Merge Shortest Paths to GC Roots選擇最小GC的路徑,然後選擇exclude all phantom/weak/soft etc.references去除一些軟,弱引用等對象,找到真正的強引用對象。如下圖:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

選擇之後,得出一份如下分析結果:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

明顯可以得出是由于$7的這個匿名内部類持有了Activity對象,導緻出現了記憶體洩漏。剛好遇到的是匿名内部類,無法得知該内部類到底是誰。這時我們隻能通過代碼查找來檢視哪個内部類出現了問題。

Dominator Tree

Dorminator Tree意味支配樹,從名稱就可以看出Dorminator Tree更善于去分析對象的引用關系。注重的是引用關系。

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

同樣我們通過在Regex過濾出XXActivity對象

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

可以發現有好幾個RecentsActivity的對象,同樣可以斷定是RecentsActivity出現了洩露。這邊有三種方式分析誰洩露:1、發現RecentsActivity7出現了洩露。2、通過Path to GC Roots的找出GC roots路徑方式來如圖:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

3、通過Merge Shortest Paths to GC Roots的方式,該方式上面已經分析過了就不繼續展開了。

以上通過兩種方法查找出了記憶體洩露的内容。

五、如何通過Android Profiler分析記憶體洩露

Memory Profiler 是 Android Profiler 中的一個元件,可幫助您識别導緻應用卡頓、當機甚至崩潰的記憶體洩漏和流失。 它顯示一個應用記憶體使用量的實時圖表,讓您可以捕獲堆轉儲、強制執行垃圾回收以及跟蹤記憶體配置設定。

下面我們通過一個例子來總結記憶體洩露問題的分析過程:

首先我們操作記憶體洩露的必現路徑,通過上面介紹的AS抓hprof的方式來抓取洩露hprof檔案。

然後就會直接顯示出目前的JAVA對象以及引用的情況。我們可以選擇左上角Arrayge by class下拉彈窗來顯示app heap的排列方式:

通過Class來排序:基于類名稱對所有配置設定進行分組。

通過Package來排序:基于軟體包名稱對所有配置設定進行分組。

通過CallbackBack:将所有配置設定分組到其對應的調用堆棧。

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

然後選擇左上角的向上圖示導出hprof檔案:

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

然後重新将hprof檔案通過AS打開,選擇右上角的Analyzer Tasks

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

在點選那個播放按鈕可以自動分析出Activity與String洩露的内容。然後選擇其中一個Activity就可以看出該Activity被誰給持有

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

image

發現同樣也是RecentsActivity

android 記憶體洩露分析方法,Android記憶體洩露的分析方法

7對象持有導緻的洩露。可以注意看持有Activity那個對象會是藍色顯示的,且前面有也有類似于MAT小黃點估計與MAT的小黃點是一個意思。

六、參考連結