Android內(nèi)存泄露的分析方法

前言

內(nèi)存泄露,這個(gè)耳熟能詳?shù)囊粋€(gè)bug,每次我在項(xiàng)目中遇到這種問題都非常的頭疼,不像普通的異常奔潰比如JE什么的,只要通過Log中的異常堆棧就可以輕松的定位出問題點(diǎn)。那么到底什么是內(nèi)存泄漏呢?簡(jiǎn)單粗俗的講,就是該被釋放的對(duì)象沒有釋放,一直被某個(gè)或某些實(shí)例所持有卻不再被使用導(dǎo)致 GC 不能回收。導(dǎo)致這種情況原因是生命周期長(zhǎng)的對(duì)象持有了生命周期短的對(duì)象導(dǎo)致的生命周期短的對(duì)象無法被回收。要想定位出內(nèi)存泄露問題,我們可以通過目前主流的MAT,Android Profiler,LeakCanary等工具來定位問題。

一、MAT簡(jiǎn)介

MAT是專門用于分析內(nèi)存的工具,在Eclipse中只要安裝相應(yīng)的插件即可。用于解析反應(yīng)當(dāng)前設(shè)備內(nèi)存映像的hprof文件,可以很直觀的開出當(dāng)前抓到的內(nèi)存情況。一般該文件包含如下內(nèi)容:

  • 所有的對(duì)象信息,包括對(duì)象實(shí)例、成員變量、存儲(chǔ)于棧中的基本類型值和存儲(chǔ)于堆中的其他對(duì)象的引用值。
  • 所有的類信息,包括classloader、類名稱、父類、靜態(tài)變量等
  • GCRoot到所有的這些對(duì)象的引用路徑
  • 線程信息,包括線程的調(diào)用棧及此線程的線程局部變量(TLS)

二、Android Profiler簡(jiǎn)介

Android Studio 3.0 采用全新的Android Profiler窗口取代 Android Monitor 工具。 這些全新的分析工具能夠提供關(guān)于應(yīng)用 CPU、內(nèi)存和網(wǎng)絡(luò) Activity 的實(shí)時(shí)數(shù)據(jù)。 您可以執(zhí)行基于樣本的函數(shù)跟蹤來記錄代碼執(zhí)行時(shí)間、采集堆轉(zhuǎn)儲(chǔ)數(shù)據(jù)、查看內(nèi)存分配,以及查看網(wǎng)絡(luò)傳輸文件的詳情。

三、如何抓取hprof文件

hprof文件有三種方式來獲?。?、DDMS抓取。2、adb命令行抓取,3、Android Studio抓取。有時(shí)抓取到的hprof格式并不是標(biāo)準(zhǔn)的可以使用如下命令轉(zhuǎn)換

hprof-conv <source path + source + filename> <out path + out + filename>

1、DDMS抓?。?/h5>

image

如上圖所示可以快速抓取到SystmeUI當(dāng)前內(nèi)存情況的hprof文件。

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

1、adb shell am dumpheap <pid/processname> <out path + filename>(必須要指定out目錄)
2、adb shell pull <path + xx.hprof> <out path>
通過以上兩個(gè)命令即可抓取到hprof文件

3、Android Studio抓?。ɑ贏ndroid 3.0以上):

首先打開Android Profier窗口我們可以看到如下的內(nèi)容:

image

第一部分用于展示CPU的使用狀態(tài),第二部分用于顯示內(nèi)存使用情況,第三部分展示網(wǎng)絡(luò)狀態(tài)。今天我們的重點(diǎn)是第二部分的內(nèi)存(MEMORY)。點(diǎn)擊MEMORY進(jìn)入專門的內(nèi)存展示界面,如圖:
image

該界面可以實(shí)時(shí)的展示當(dāng)前進(jìn)程內(nèi)存的使用情況,包含了JAVA,NATIVE等內(nèi)存。并通過不同的顏色來區(qū)分,可以非常方便的查看分析問題。如果知道內(nèi)存泄露的必現(xiàn)路徑,可以在該界面清晰的看到內(nèi)存的增長(zhǎng)。
然后通過如下方法就可以dump出hprof文件:
image

通過Android Profier生成的hprof文件也不是標(biāo)準(zhǔn)的,AS提供了便捷的轉(zhuǎn)換方式:AS提供了便捷的轉(zhuǎn)換方式:Memory Monitor生成的hpof文件都會(huì)顯示在AS左側(cè)的Captures標(biāo)簽中,在Captures標(biāo)簽中選擇要轉(zhuǎn)換的hpof文件,并點(diǎn)擊鼠標(biāo)右鍵,在彈出的菜單中選擇Export to standard.hprof選項(xiàng),即可導(dǎo)出標(biāo)準(zhǔn)的hpof文件,如下圖所示。
image

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

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

通過MAT打開hprof文件,選擇Leak Suspects Report選項(xiàng),就會(huì)生產(chǎn)分析報(bào)告。主要分為兩個(gè)部分:1、Overview。2、Leak Suspects(泄漏猜想)。如下如所示:

image

該分析報(bào)告可以基本看到內(nèi)存的使用情況,以及一些泄漏猜想(一般定位不出問題來)。主要是通過查看Histogram 或者 Dominator Tree來分析問題。

Histogram

首先我們先說說Histogram。Histogram主要通過類的角度分析,注重的是量的分析。通過點(diǎn)擊工具欄上

image
按鈕選擇柱狀圖方式分析。內(nèi)容如圖:
image

主要有四個(gè)方面的內(nèi)容。1、Class Name。2、Objects(該類的個(gè)數(shù))。3、ShallowHeap(該類本身所占用的大?。?。4、Retained Heap。
Retained Heap:一個(gè)對(duì)象的Retained Set所包含對(duì)象所占內(nèi)存的總大小。換句話說,Retained Heap就是當(dāng)前對(duì)象被GC后,從Heap上總共能釋放掉的內(nèi)存。(Retained Set指的是這個(gè)對(duì)象本身和他持有引用的對(duì)象以及這些引用對(duì)象的Retained Set所占內(nèi)存大小的總和)官方的圖解如下所示:
image

從圖中可以看出E的Retained Set為E和G。C的Retained Set為C、D、E、F、G、H。
MAT所定義的支配樹就是從上圖的引用樹演化而來。在引用樹當(dāng)中,一條到Y(jié)的路徑必然會(huì)經(jīng)過X,這就是X支配Y。X直接支配Y則指的是在所有支配Y的對(duì)象中,X是Y最近的一個(gè)對(duì)象。支配樹就是反映的這種直接支配關(guān)系,在支配樹中,父節(jié)點(diǎn)直接支配子節(jié)點(diǎn)。下圖就是官方提供的一個(gè)從引用樹到支配樹的轉(zhuǎn)換示意圖。
image

C直接支配D、E,因此C是D、E的父節(jié)點(diǎn),這一點(diǎn)根據(jù)上面的闡述很容易得出結(jié)論。C直接支配H,這可能會(huì)有些疑問,能到達(dá)H的主要有兩條路徑,而這兩條路徑FD和GE都不是必須要經(jīng)過的節(jié)點(diǎn),只有C滿足了這一點(diǎn),因此C直接支配H,C就是H的父節(jié)點(diǎn)。通過支配樹,我們就可以很容易的分析一個(gè)對(duì)象的Retained Set,比如E被回收,則會(huì)釋放E、G的內(nèi)存,而不會(huì)釋放H的內(nèi)存,因?yàn)镕可能還引用著H,只有C被回收,H的內(nèi)存才會(huì)被釋放。
假如說我們要找出Activity的情況,我們可以在Regex中輸入XXActivity過濾條件來搜索。Regex支持正則表達(dá)式
image

RecentActivity以及其他各種內(nèi)部類對(duì)象(Activity后面的美元符號(hào)代表的是內(nèi)部類)有3個(gè),可以斷定Activity出現(xiàn)了泄漏。具體查看為啥導(dǎo)致了內(nèi)存泄漏,可以通過右擊選擇Merge Shortest Paths to GC Roots選擇最小GC的路徑,然后選擇exclude all phantom/weak/soft etc.references去除一些軟,弱引用等對(duì)象,找到真正的強(qiáng)引用對(duì)象。如下圖:
image

選擇之后,得出一份如下分析結(jié)果:
image

明顯可以得出是由于$7的這個(gè)匿名內(nèi)部類持有了Activity對(duì)象,導(dǎo)致出現(xiàn)了內(nèi)存泄漏。剛好遇到的是匿名內(nèi)部類,無法得知該內(nèi)部類到底是誰(shuí)。這時(shí)我們只能通過代碼查找來查看哪個(gè)內(nèi)部類出現(xiàn)了問題。

Dominator Tree

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

image

同樣我們通過在Regex過濾出XXActivity對(duì)象
image

可以發(fā)現(xiàn)有好幾個(gè)RecentsActivity的對(duì)象,同樣可以斷定是RecentsActivity出現(xiàn)了泄露。這邊有三種方式分析誰(shuí)泄露:1、發(fā)現(xiàn)RecentsActivity7出現(xiàn)了泄露。2、通過Path to GC Roots的找出GC roots路徑方式來如圖:
image

3、通過Merge Shortest Paths to GC Roots的方式,該方式上面已經(jīng)分析過了就不繼續(xù)展開了。
以上通過兩種方法查找出了內(nèi)存泄露的內(nèi)容。

五、如何通過Android Profiler分析內(nèi)存泄露

Memory Profiler 是 Android Profiler 中的一個(gè)組件,可幫助您識(shí)別導(dǎo)致應(yīng)用卡頓、凍結(jié)甚至崩潰的內(nèi)存泄漏和流失。 它顯示一個(gè)應(yīng)用內(nèi)存使用量的實(shí)時(shí)圖表,讓您可以捕獲堆轉(zhuǎn)儲(chǔ)、強(qiáng)制執(zhí)行垃圾回收以及跟蹤內(nèi)存分配。

下面我們通過一個(gè)例子來總結(jié)內(nèi)存泄露問題的分析過程:

  1. 首先我們操作內(nèi)存泄露的必現(xiàn)路徑,通過上面介紹的AS抓hprof的方式來抓取泄露hprof文件。
  2. 然后就會(huì)直接顯示出當(dāng)前的JAVA對(duì)象以及引用的情況。我們可以選擇左上角Arrayge by class下拉彈窗來顯示app heap的排列方式:
  • 通過Class來排序:基于類名稱對(duì)所有分配進(jìn)行分組。
  • 通過Package來排序:基于軟件包名稱對(duì)所有分配進(jìn)行分組。
  • 通過CallbackBack:將所有分配分組到其對(duì)應(yīng)的調(diào)用堆棧。
    image
  1. 然后選擇左上角的向上圖標(biāo)導(dǎo)出hprof文件:
    image

    然后重新將hprof文件通過AS打開,選擇右上角的Analyzer Tasks
    image

    在點(diǎn)擊那個(gè)播放按鈕可以自動(dòng)分析出Activity與String泄露的內(nèi)容。然后選擇其中一個(gè)Activity就可以看出該Activity被誰(shuí)給持有
    image

    發(fā)現(xiàn)同樣也是RecentsActivity7持有,打開多個(gè)Activity對(duì)象發(fā)現(xiàn)都是這個(gè)RecentsAcitivity7對(duì)象持有導(dǎo)致的泄露??梢宰⒁饪闯钟蠥ctivity那個(gè)對(duì)象會(huì)是藍(lán)色顯示的,且前面有也有類似于MAT小黃點(diǎn)估計(jì)與MAT的小黃點(diǎn)是一個(gè)意思。

六、參考鏈接

  1. Android內(nèi)存優(yōu)化(五)詳解內(nèi)存分析工具M(jìn)AT
  2. Android內(nèi)存優(yōu)化(三)避免可控的內(nèi)存泄漏
  3. Android 內(nèi)存泄漏總結(jié)
  4. 使用MAT工具分析性能(有些左下角有小黃點(diǎn),帶有這個(gè)小黃點(diǎn)的就表示可以被GC ROOT訪問到,可以被GC Root訪問到的對(duì)象是不可以被回收的,當(dāng)然不代表帶有小黃點(diǎn)的就是內(nèi)存泄漏的,也有些是一直被系統(tǒng)所使用的。其實(shí)也不算內(nèi)存泄漏因?yàn)樵谶@三個(gè)后面都是顯示的System Class,代表著它被系統(tǒng)所占用)
  5. Android性能優(yōu)化:徹底解決內(nèi)存泄漏
  6. HPROF Viewer and Analyzer
  7. 使用 Memory Profiler 查看 Java 堆和內(nèi)存分配
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 內(nèi)存泄露,這個(gè)耳熟能詳?shù)囊粋€(gè)bug,每次我在項(xiàng)目中遇到這種問題都非常的頭疼,不像普通的異常奔潰比如JE什么的...
    不正就是歪573閱讀 1,515評(píng)論 0 51
  • 本文主要有以下三部分內(nèi)容:第一部分:簡(jiǎn)單介紹開發(fā)者指南上內(nèi)存相關(guān)的文章。第二部分:總結(jié)移動(dòng)App性能評(píng)測(cè)與優(yōu)化內(nèi)存...
    htkeepmoving閱讀 4,258評(píng)論 0 5
  • 一、 Android的內(nèi)存機(jī)制 Android的程序由Java語(yǔ)言編寫,所以Android的內(nèi)存管理與Java的內(nèi)...
    陳大沖閱讀 1,156評(píng)論 0 4
  • 1、內(nèi)存了解 在Android App的性能優(yōu)化的各個(gè)部分里,內(nèi)存方面的知識(shí)較多且不易理解,內(nèi)存的問題絕對(duì)是最令人...
    蕭竹閱讀 9,074評(píng)論 1 12
  • 一、Android內(nèi)存分析基礎(chǔ) 1、查看進(jìn)程分配的最大內(nèi)存 每個(gè)App進(jìn)程可以分配到的最大內(nèi)存是有限的,當(dāng)然不同型...
    礪雪凝霜閱讀 2,331評(píng)論 0 4

友情鏈接更多精彩內(nèi)容