欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Android內(nèi)存泄漏檢測與MAT使用

 更新時間:2016年12月22日 10:37:29   作者:自在時刻  
編寫沒有內(nèi)存泄漏的程序,對提高程序穩(wěn)定性,提高用戶體驗(yàn)具有重要的意義。這篇文章主要介紹了詳解Android內(nèi)存泄漏檢測與MAT使用,有興趣的可以了解一下。

內(nèi)存泄漏基本概念

內(nèi)存檢測這部分,相關(guān)的知識有JVM虛擬機(jī)垃圾收集機(jī)制,類加載機(jī)制,內(nèi)存模型等。編寫沒有內(nèi)存泄漏的程序,對提高程序穩(wěn)定性,提高用戶體驗(yàn)具有重要的意義。因此,學(xué)習(xí)Java利用java編寫程序的時候,要特別注意內(nèi)存泄漏相關(guān)的問題。雖然JVM提供了自動垃圾回收機(jī)制,但是還是有很多情況會導(dǎo)致內(nèi)存泄漏。

內(nèi)存泄漏主要原因就是一個生命周期長的對象,持有了一個生命周期短的對象的引用。這樣,會導(dǎo)致短的對象在該回收時候無法被回收。Android中比較典型的有:1、靜態(tài)變量持有Activity的context。2、或者Handler持有某個組件的context,同時如果Looper的消息隊(duì)列中有針對該Handler的消息沒有被處理,那么會被作為target持有強(qiáng)引用,最終的導(dǎo)致context無法釋放,導(dǎo)致相應(yīng)組件在退出時無法被內(nèi)存回收。3、非靜態(tài)內(nèi)部類默認(rèn)持有外部類的引用,這樣如果我們在Activity中定義了一個Thread內(nèi)部類,同時直接通過new Thread的方式去運(yùn)行線程,那么在線程運(yùn)行結(jié)束之前,線程都會持有Activity的引用,從而導(dǎo)致Activity無法被釋放。

內(nèi)存檢測工具

LeakCananry

LeakCanary,主要監(jiān)測的是使用過程中Activity,F(xiàn)ragment等組件是否沒被內(nèi)存回收。使用方法也十分簡單,相當(dāng)于裝了一個監(jiān)聽器,然后通過正常 操作去尋找內(nèi)存泄漏,發(fā)生內(nèi)存泄漏的時候會有Toast,同時可以在相應(yīng)程序查看哪里發(fā)生內(nèi)存泄漏。
方法比較簡單,添加leakcanary依賴以后,新建一個Application入口,在Oncreate方法中安裝Leakcanary即可。

這里寫圖片描述

當(dāng)發(fā)生內(nèi)存泄漏時,屏幕會出現(xiàn)Toast,同時打開桌面上的Leaks程序,顯示泄漏的內(nèi)存,如下圖:

這里寫圖片描述

LeakCananry實(shí)現(xiàn)步驟大致是:

實(shí)現(xiàn)大致步驟是:

1、自動把a(bǔ)ctivity加入到KeyedWeakReference

2、在background線程中,檢查onDestroy后reference是否被清除,且沒有觸發(fā)gc

3、如果reference沒有被清除,則dump heap到一個hprof文件并保存到app文件系統(tǒng)中

4、在一個單獨(dú)進(jìn)程中啟動HeapAnalyzerService,HeapAnalyzer使用HAHA來分析heap dump。

5、HeapAnalyzer在heap dump中根據(jù)reference key找到KeyedWeakReference。

6、HeapAnalyzer計算出到GC Roots的最短強(qiáng)引用路徑來判斷是否存在泄露,然后build出造成這個泄露的引用鏈。

7、結(jié)果被傳回來app進(jìn)程的DisplayLeakService,并展示一個泄露的notification。

方法的有點(diǎn)是簡單易行,但是只能檢測Activity、Fragment是否發(fā)生內(nèi)存泄漏。

觀看整體內(nèi)存使用情況

詳情參見官方文檔: https://developer.android.com/studio/profile/investigate-ram.html#ViewingAllocations

使用adb shell,進(jìn)入手機(jī)adb,執(zhí)行命令:

dumpsys meminfo <包名> [-參數(shù)]

可以查看應(yīng)用不同部分內(nèi)存分配情況。比如Java heap,Native heap等

輸出是目前具體應(yīng)用的內(nèi)存分配,單位是kilobytes

因?yàn)槌绦蛏婕癹ni,經(jīng)常會分配本地內(nèi)存,所以會使用adb shell 的方式去查看native heap的分配情況。

結(jié)果如下:

這里寫圖片描述

分析各個參數(shù):

Private Clean/Dirty RAM:

這部分內(nèi)存是app的私有內(nèi)存,當(dāng)app銷毀是操作系統(tǒng)可以回收到的內(nèi)存。其中private dirty只能被你的進(jìn)程使用,同時只能存在在內(nèi)存當(dāng)中,當(dāng)內(nèi)存不夠,也不能通過分頁技術(shù)存儲到硬盤(操作系統(tǒng)相關(guān)知識),dalvik和native heap上的分配都是private dirty RAM。因?yàn)槭莇alvik heap和native heap共享的內(nèi)存,所以命名dirty?

DDMS

使用流程

  • 啟動eclipse后,切換到DDMS透視圖,并確認(rèn)Devices視圖、Heap視圖都是打開的;
  • 將手機(jī)通過USB鏈接至電腦,鏈接時需要確認(rèn)手機(jī)是處于“USB調(diào)試”模式,而不是作為“MassStorage”;
  • 鏈接成功后,在DDMS的Devices視圖中將會顯示手機(jī)設(shè)備的序列號,以及設(shè)備中正在運(yùn)行的部分進(jìn)程信息;
  • 點(diǎn)擊選中想要監(jiān)測的進(jìn)程,比如system_process進(jìn)程;
  • 點(diǎn)擊選中Devices視圖界面中最上方一排圖標(biāo)中的“Update Heap”圖標(biāo);
  • 點(diǎn)擊Heap視圖中的“Cause GC”按鈕;
  • 此時在Heap視圖中就會看到當(dāng)前選中的進(jìn)程的內(nèi)存使用量的詳細(xì)情況。

如何檢測內(nèi)存泄漏?

Heap視圖中部有一個Type叫做dataobject,即數(shù)據(jù)對象,也就是我們的程序中實(shí)例化的對象。在data object一行中有一列是“Total Size”,其值就是當(dāng)前進(jìn)程中所有Java數(shù)據(jù)對象的內(nèi)存總量,一般情況下,這個值的大小決定了是否會有內(nèi)存泄漏。

正常情況下Total Size值都會穩(wěn)定在一個有限的范圍內(nèi),也就是說沒有造成對象不被垃圾回收的情況,所以說雖然我們不斷的操作會不斷的生成很多對象,而在虛擬機(jī)不斷的進(jìn)行GC的過程中,這些對象都被回收了,內(nèi)存占用量會會落到一個穩(wěn)定的水平。如果代碼中存在沒有釋放對象引用的情況,則dataobject的Total Size值在每次GC后不會有明顯的回落,隨著操作次數(shù)的增多Total Size的值會越來越大

通過DDMS方式,DataObject 的totalSize如果穩(wěn)定在一個大概范圍內(nèi),則可以確定沒有發(fā)生內(nèi)存泄漏。

MAT

然而,并不是所有的內(nèi)存泄漏都十分明顯,并且會最終導(dǎo)致OOM。有時候只有幾個對象被泄漏,雖然影響不大,但是無疑浪費(fèi)了內(nèi)存。

要發(fā)現(xiàn)這種比較隱蔽的內(nèi)存泄漏,我們需要使用MAT工具。

在了解支配樹之前,要先了解一些相關(guān)概念。

支配樹

支配樹體現(xiàn)了對象實(shí)例間的支配關(guān)系,在對象引用圖中,所有指向?qū)ο驜的路徑都經(jīng)過對象A,則認(rèn)為對象A支配對象B。

這里寫圖片描述 

在這張圖里,左邊是對象引用關(guān)系,對于A和B,要抵達(dá)這兩個點(diǎn)必須經(jīng)過GC root。而對于C可以從A也可以從B抵達(dá),但都必須經(jīng)過GC root,所以最近的支配點(diǎn)同樣也是GC root。

對于點(diǎn)D,不管是從C->D還是C->D->F->D,都必須經(jīng)過的最近的點(diǎn)是C,所以C是D的支配點(diǎn)。同理可得EFHG在支配樹中的位置。

SHALLOWHEAP和RETAINED HEAP

Shallow heap表示對象本身所占內(nèi)存大小,一個內(nèi)存大小100bytes的對象Shallow heap就是100bytes。

Retained heap表示通過回收這一個對象總共能回收的內(nèi)存,比方說一個100bytes的對象還直接或者間接地持有了另外3個100bytes的對象引用,回收這個對象的時候如果另外3個對象沒有其他引用也能被回收掉的時候,Retained heap就是400bytes。

在使用mat進(jìn)行分析時,我們常常接觸到的數(shù)據(jù)就是shallow size和retained size: Shallow Size

對象自身占用的內(nèi)存大小,不包括它引用的對象。

針對非數(shù)組類型的對象,它的大小就是對象與它所有的成員變量大小的總和。當(dāng)然這里面還會包括一些java語言特性的數(shù)據(jù)存儲單元。

針對數(shù)組類型的對象,它的大小是數(shù)組元素對象的大小總和。

Retained Size

Retained Size=當(dāng)前對象大小+當(dāng)前對象可直接或間接引用到的對象的大小總和。(間接引用的含義:A->B->C, C就是間接引用)
換句話說,Retained Size就是當(dāng)前對象被GC后,從Heap上總共能釋放掉的內(nèi)存。
不過,釋放的時候還要排除被GC Roots直接或間接引用的對象。他們暫時不會被回收。如下圖:

這里寫圖片描述

A對象的Retained Size=A對象的Shallow Size

B對象的Retained Size=B對象的Shallow Size + C對象的Shallow Size

因?yàn)锽對象被釋放時,C同時被釋放,而D由于被GC roots直接引用所以不會被釋放。而Retained Size就是當(dāng)前對象被GC后,從Heap上總共能釋放掉的內(nèi)存。

以上概念,都是在使用MAT進(jìn)行內(nèi)存分析經(jīng)常使用的,所以要記住。

MAT的下載與使用

下載地址:https://eclipse.org/mat/downloads.php

這里沒有作為eclipse插件的方式下載mat,而是通過下載單獨(dú)的軟件客戶端。

首先,在DDMS中選擇要檢測的進(jìn)程并dump HPROF file,如下圖:

這里寫圖片描述

HPROF中存儲的是當(dāng)前內(nèi)存的快照,因此,在dump快照之前先點(diǎn)擊cause GC手動觸發(fā)一次垃圾回收,這樣可以避免軟引用、弱引用等不必要的對象保留在內(nèi)存中影響我們的分析。

轉(zhuǎn)儲出來的hprof文件,還有使用sdk自帶工具進(jìn)行一下格式轉(zhuǎn)化,工具在sdk路徑下的platform-tools下,名稱為hprof-conv。

使用方法:

/.hprof-conv.exe a.hprof b.hprof

a 是輸入hprof文件名,b是輸出文件名。

然后將b.hprof在eclipse memory Analyzer中打開,注意要轉(zhuǎn)換格式,不然無法成功打開。

如下:

這里寫圖片描述

利用MAT分析內(nèi)存泄漏

分析過程中,主要使用的是Histogram直方圖,和Dominater tree支配樹。

在Histogram視圖中查找retained heap值最大的項(xiàng),并分析這里是否發(fā)生內(nèi)存泄漏。

這里寫圖片描述

注意,一般情況下我們忽略java、android系統(tǒng)自帶的對象,而著重分析我們自己程序中的對象。所以在上面輸入過濾Class Name。

Retained heap表示因?yàn)檫@個對象,會導(dǎo)致多少對象無法回收。

右擊相應(yīng)類,list objects->with incoming references。表明引用這個類的某個實(shí)例的其它類,也就是它在引用樹中的父節(jié)點(diǎn)。通過分析該對象被誰引用,來判斷為何沒被垃圾回收。
outcoming reference就是子節(jié)點(diǎn),查看一些當(dāng)前對象引用著的對象。

此外看,Merge shortest path to gc root,可以找到一條到GC root的最短路徑,來看為什么當(dāng)前對象無法被回收。

實(shí)戰(zhàn)分析

下面記錄了本人對一個項(xiàng)目的具體分析過程,以及各個工具的使用方法。

1、使用DDMS查看內(nèi)存

使用DDMS的過程中,針對應(yīng)用分別進(jìn)行了多次檢測,主要查看程序運(yùn)行前的內(nèi)存使用情況和程序運(yùn)行后的內(nèi)存使用情況:

使用前:

這里寫圖片描述

使用后:

這里寫圖片描述

通過上述數(shù)據(jù)可以看到,在程序運(yùn)行前data object也就是在堆上分配的數(shù)據(jù)是180KB左右,而運(yùn)行后內(nèi)存大概在300KB上下浮動,沒有呈現(xiàn)一個明顯的一直上升的情況,故而沒有明顯的內(nèi)存泄漏,基本沒有導(dǎo)致OOM的可能。

但是,可以發(fā)現(xiàn),程序運(yùn)行一次以后,放置一段時間,即便手動觸發(fā)GC,堆上的內(nèi)存雖然回落,但是仍然是288KB,與執(zhí)行前的180KB相差較大,說明有一些對象被GC roots引用,無法完成釋放。

下面采用MAT工具進(jìn)行進(jìn)一步分析。在上面的過程中,轉(zhuǎn)出了三個hprof文件,將hprof文件利用Android sdk tools下的工具進(jìn)行格式轉(zhuǎn)換,進(jìn)行對比分析:

這里寫圖片描述

2、使用MAT分析內(nèi)存轉(zhuǎn)儲

前面分析內(nèi)存使用發(fā)現(xiàn),使用前和使用后有一個100KB左右的差值,同時即便放置一段時間仍然無法使用。將before和after的直方圖加入對比欄,在MAT中進(jìn)行對比:

這里寫圖片描述

點(diǎn)擊右上角的紅色嘆號:

這里寫圖片描述

這里寫圖片描述

對比發(fā)現(xiàn)兩個shallow heap大小基本相同,多出的部分是UpdatePartResultThread,系統(tǒng)類而不是我們自己編寫程序造成的。
再看一下使用前后直方圖中的retained heap:

這里寫圖片描述

可以看出,程序執(zhí)行后,newActivity強(qiáng)引用了一些對象,在newAcitivity沒有推出前,retainedheap部分內(nèi)存無法被回收。這也就是我們在DDMS中發(fā)現(xiàn)堆內(nèi)存差異的主要原因。

右擊直方圖中的NewActivity,可以看見如下選項(xiàng):

這里寫圖片描述

用的比較多的是List objects和Merger shortest Paths to GC Roots。

List objects:

Outgoing reference是支配樹中當(dāng)前對象的子節(jié)點(diǎn),也就是當(dāng)前對象持有哪些引用。

Incoming reference是父節(jié)點(diǎn),即當(dāng)前對象被誰引用,為什么沒被回收。

Merger shortest Paths to GC Roots:找到當(dāng)前無法被釋放的對象到GC roots的最短路徑。即排查當(dāng)前對象被誰引用,為什么沒有被釋放。這里因?yàn)槲覀兊膶ο笫且粋€Activity,當(dāng)它顯示在前臺的時候,不會被垃圾回收,所以不是我們分析的點(diǎn)。

在這里,我們查看outgoing reference,查看當(dāng)前對象擁有哪些強(qiáng)引用:

這里寫圖片描述

排除系統(tǒng)的對象,還是主要分析我們編寫的程序。

這里寫圖片描述

最后發(fā)現(xiàn),我們在之前使用LeakCanary時,注冊的相應(yīng)監(jiān)聽器沒有回收,發(fā)現(xiàn)了內(nèi)存泄漏 :)。

去掉LeakCanary,再次測試發(fā)現(xiàn)data object的值確實(shí)下降了不少。

繼續(xù)分析,發(fā)現(xiàn)newActivity引用了一個

這里寫圖片描述

致使一部分內(nèi)存無法被釋放。這個問題屬于客戶端實(shí)現(xiàn)問題,不在內(nèi)存泄漏的范圍內(nèi)。

接下來,在直方圖中過濾出服務(wù)端的類:

這里寫圖片描述 

可以看到,服務(wù)端的類大部分shallow heap都為0,也就是已經(jīng)被垃圾回收。

結(jié)論

在使用MAT分析內(nèi)存時,最關(guān)鍵的就是找引用關(guān)系。如果一個應(yīng)該被釋放的對象沒有被釋放,那么我們往往要查看它的incoming reference,看看是誰持有了它的強(qiáng)引用。同時利用Merger shortest GC roots找到到GC root的最短路徑,確定是由于被誰引用而導(dǎo)致無法GC。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • android 下載時文件名是中文和空格會報錯解決方案

    android 下載時文件名是中文和空格會報錯解決方案

    項(xiàng)目中遇到了下載文件文件名是中文而且還有空格如果不對連接進(jìn)行處理下載就會報錯要想解決這個問題只需對你的url進(jìn)行編碼然后替換空格用編碼表示,感興趣的朋友可以詳細(xì)了解下
    2013-01-01
  • 最新評論