性能優(yōu)化工具知識(shí)梳理(5) - MAT

性能優(yōu)化工具知識(shí)梳理(1) - TraceView
性能優(yōu)化工具知識(shí)梳理(2) - Systrace
性能優(yōu)化工具知識(shí)梳理(3) - 調(diào)試GPU過(guò)度繪制 & GPU呈現(xiàn)模式分析
性能優(yōu)化工具知識(shí)梳理(4) - Hierarchy Viewer
性能優(yōu)化工具知識(shí)梳理(5) - MAT
性能優(yōu)化工具知識(shí)梳理(6) - Memory Monitor & Heap Viewer & Allocation Tracker
性能優(yōu)化工具知識(shí)梳理(7) - LeakCanary
性能優(yōu)化工具知識(shí)梳理(8) - Lint

一、概述

內(nèi)存一直都是性能優(yōu)化的重點(diǎn),今天我們主要介紹如何使用Android Studio生成分析hprof報(bào)表混狠,并使用MAT分析結(jié)果桐腌,在介紹之前,首先需要感謝Gracker裁蚁,本文的分析大多數(shù)都是來(lái)自于它的這篇文章:

http://www.reibang.com/p/d8e247b1e7b2

二矢渊、獲取內(nèi)存快照并分析

2.1 獲取內(nèi)存快照

為了便于大家理解,我們先編寫一個(gè)用于調(diào)試的單例MemorySingleton枉证,它內(nèi)部包含一個(gè)成員變量ObjectA矮男,而ObjectA又包含了ObjectBObjectC,以及一個(gè)長(zhǎng)度為4096int數(shù)組室谚,ObjectBObjectC各自包含了一個(gè)ObjectD毡鉴,ObjectD中包含了一個(gè)長(zhǎng)度為4096int數(shù)組崔泵,在ActivityonCreate()中,我們初始化這個(gè)單例對(duì)象猪瞬。

public class MemorySingleton {
    private static MemorySingleton sInstance;
    private ObjectA objectA;
    
    public static synchronized MemorySingleton getInstance() {
        if (sInstance == null) {
            sInstance = new MemorySingleton();
        }
        return sInstance;
    }

    private MemorySingleton() {
        objectA = new ObjectA();
    }

}

public class ObjectA {
    private int[] dataOfA = new int[4096];
    private ObjectB objectB = new ObjectB();
    private ObjectC objectC = new ObjectC();
}

public class ObjectB {
    private ObjectD objectD = new ObjectD();
}

public class ObjectC {
    private ObjectD objectD = new ObjectD();
}

public class ObjectD {
    private int[] dataOfD = new int[4096];
}

public class MemoryActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory);
        MemorySingleton.getInstance();
    }
}

Android Studio最下方的Monitors/Memory一欄中憎瘸,可以看到應(yīng)用占用的內(nèi)存數(shù)值,它的界面分為幾個(gè)部分:


我們先點(diǎn)擊2主動(dòng)觸發(fā)一次垃圾回收陈瘦,再點(diǎn)擊3來(lái)獲得內(nèi)存快照幌甘,等待一段時(shí)間后,會(huì)在窗口的左上部分獲得后綴為hprof的分析報(bào)表:

在生成的hprof上點(diǎn)擊右鍵甘晤,就可以導(dǎo)出為標(biāo)準(zhǔn)的hprof用于MAT分析含潘,在屏幕的右邊,Android Studio也提供了分析的界面线婚,今天我們先不介紹它遏弱,而是導(dǎo)出成MAT可識(shí)別的分析報(bào)表。

2.2 使用MAT分析報(bào)表

運(yùn)行MAT塞弊,打開(kāi)我們導(dǎo)出的標(biāo)準(zhǔn)hprof文件:


經(jīng)過(guò)一段時(shí)間的轉(zhuǎn)換之后漱逸,會(huì)得到下面的Overview界面,我們主要關(guān)注的是Actions部分:

Actions包含了四個(gè)部分游沿,點(diǎn)擊它可以得到不同的視圖:

  • Histogram
  • Dominator Tree
  • Top Consumers
  • Duplicate Classes

在平時(shí)的分析當(dāng)中饰抒,主要用到前兩個(gè)視圖,下面我們就來(lái)依次看一下怎么使用這兩個(gè)視圖來(lái)進(jìn)行分析內(nèi)存的使用情況诀黍。

2.2.1 Histogram

點(diǎn)開(kāi)Histogram之后袋坑,會(huì)得到以下的界面:


這個(gè)視圖中提供了多種方式來(lái)對(duì)對(duì)象進(jìn)行分類,這里為了分析方便眯勾,我們選擇按包名進(jìn)行分類:

要理解這個(gè)視圖枣宫,最關(guān)鍵的是要明白紅色矩形框中各列的含義,下面吃环,我們就以在MemorySingleton中定義的成員對(duì)象為例也颤,來(lái)一起理解一下它們的含義:

  • Objects:表示該類在內(nèi)存當(dāng)中的對(duì)象個(gè)數(shù)
    這一列比較好理解郁轻,ObjectA包含了ObjectBObjectC兩個(gè)成員變量翅娶,而它們又各自包含了ObjectD,因此內(nèi)存當(dāng)中有2個(gè)ObjectD對(duì)象好唯。
  • Shallow Heap
    這一列中文翻譯過(guò)來(lái)是“淺堆”竭沫,表示的是對(duì)象自身所占用的內(nèi)存大小,不包括它所引用的對(duì)象的內(nèi)存大小骑篙。舉例來(lái)說(shuō)蜕提,ObjectA包含了int[]ObjectBObjectC這三個(gè)引用替蛉,但是它并不包括這三個(gè)引用所指向的int[]數(shù)組贯溅、ObjectB對(duì)象和ObjectC對(duì)象拄氯,它的大小為24個(gè)字節(jié),ObjectB/C/D也是同理它浅。
  • Retained Heap
    這一列中文翻譯過(guò)來(lái)是“保留堆”译柏,也就是當(dāng)該對(duì)象被垃圾回收器回收之后,會(huì)釋放的內(nèi)存大小姐霍。舉例來(lái)說(shuō)鄙麦,如果ObjectA被回收了之后,那么ObjectBObjectC也就沒(méi)有對(duì)象繼續(xù)引用它了镊折,因此它也被回收胯府,它們各自內(nèi)部的ObjectD也會(huì)被回收,如下圖所示:

    因?yàn)?code>ObjectA被回收之后恨胚,它內(nèi)部的int[]數(shù)組骂因,以及ObjectB/ObjectC所包含的ObjectDint[]數(shù)組所占用的內(nèi)存都會(huì)被回收,也就是:
retained heap of ObjectA = shallow heap of ObjectA + int[4096] +retained heap of ObjectB + retained heap of ObjectC

下面赃泡,我們考慮一種比較復(fù)雜的情況寒波,我們的引用情況變?yōu)榱讼旅孢@樣:



對(duì)應(yīng)的代碼為:

public class MemorySingleton {
    private static MemorySingleton sInstance;
    private ObjectA objectA;
    private ObjectE objectE;
    private ObjectF objectF;
    public static synchronized MemorySingleton getInstance() {
        if (sInstance == null) {
            sInstance = new MemorySingleton();
        }
        return sInstance;
    }
    private MemorySingleton() {
        objectA = new ObjectA();
        objectE = new ObjectE();
        objectF = new ObjectF();
        objectE.setObjectF(objectF);
    }
}

public class ObjectE {
    private ObjectF objectF;
    public void setObjectF(ObjectF objectF) {
        this.objectF = objectF;
    }
}

public class ObjectF {
    private int[] dataInF = new int[4096];
}

我們重新抓取一次內(nèi)存快照,那么情況就變?yōu)榱耍?br>


可以看到ObjectERetained Heap大小僅僅為16字節(jié)升熊,和它的Shallow Heap相同俄烁,這是因?yàn)樗鼉?nèi)部的成員變量objectF所引用的ObjectF,也同時(shí)被MemorySingleton中的成員變量objectF所引用级野,因此ObjectE的釋放并不會(huì)導(dǎo)致objectF對(duì)象被回收页屠。

總結(jié)一下,Histogram是從類的角度來(lái)觀察整個(gè)內(nèi)存區(qū)域的蓖柔,它會(huì)列出在內(nèi)存當(dāng)中辰企,每個(gè)類的實(shí)例個(gè)數(shù)和內(nèi)存占用情況。

分析完這三個(gè)比較重要的列含義之后渊抽,我們?cè)賮?lái)看一下通過(guò)右鍵點(diǎn)擊某個(gè)Item之后的彈出列表中的選項(xiàng):

  • List Objects
  • incomming reference表示它被那些對(duì)象所引用
  • outgoing則表示它所引用的對(duì)象
  • Show objects by class
    和上面的選項(xiàng)類似蟆豫,只不過(guò)列出的是類名议忽。
  • Merge Shortest Paths to GC Roots懒闷,我們可以選擇排除一些類型的引用:

    Gc根節(jié)點(diǎn)的最短路徑,以ObjectD為例栈幸,它的兩個(gè)實(shí)例對(duì)象到Gc Roots的路徑如下愤估,這個(gè)選項(xiàng)很重要,當(dāng)需要定位內(nèi)存泄漏問(wèn)題的時(shí)候速址,我們一般都是通過(guò)這個(gè)工具:

2.2.2 dominator_tree

dominator_tree則是通過(guò)“引用樹”的方式來(lái)展現(xiàn)內(nèi)存的使用情況的玩焰,通俗點(diǎn)來(lái)說(shuō),它是站在對(duì)象的角度來(lái)觀察內(nèi)存的使用情況的芍锚。例如下圖昔园,只有MemorySingletonRetain Heap的大小被計(jì)算出來(lái)蔓榄,而它內(nèi)部的成員變量的Retain Heap都為0


要獲得更詳細(xì)的情況,我們需要通過(guò)它的outgoing默刚,也就是它所引用的對(duì)象來(lái)分析:

可以看到它的outgoing視圖中有兩個(gè)objectF甥郑,但是它們都是指向同一片內(nèi)存空間@0x12d8d7f0,通過(guò)這個(gè)視圖荤西,我們可以列出那么占用內(nèi)存較多的對(duì)象澜搅,然后一步步地分析,看究竟是什么導(dǎo)致了它所占用如此多的內(nèi)存邪锌,以此達(dá)到優(yōu)化性能的目的勉躺。

2.3 分析Activity內(nèi)存泄漏問(wèn)題

在平時(shí)的開(kāi)發(fā)中,我們最容易遇到的就是Activity內(nèi)存泄漏觅丰,下面饵溅,我們模擬一下這種情況,并演示一下通過(guò)MAT來(lái)分析內(nèi)存泄漏的原因妇萄,首先概说,我們編寫一段會(huì)導(dǎo)致內(nèi)存泄漏的代碼:


public class MemorySingleton {
    private static MemorySingleton sInstance;
    private Context mContext;
    public static synchronized MemorySingleton getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new MemorySingleton(context);
        }
        return sInstance;
    }
    private MemorySingleton(Context context) {
        mContext = context;
    }
}

public class MemoryActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory);
        MemorySingleton.getInstance(this);
    }
}

我們啟動(dòng)MemoryActivity之后,然后按back退出嚣伐,按照常理來(lái)說(shuō)糖赔,此時(shí)它應(yīng)當(dāng)被回收,但是由于它被MemorySingleton中的mContext所引用轩端,因此它并不能被回收放典,此時(shí)的內(nèi)存快照為:


我們通過(guò)查看它到Gc Roots的引用鏈,就可以分析出它為什么沒(méi)有被回收了:

三基茵、小結(jié)

通過(guò)Android StudioMAT結(jié)合奋构,我們就可以獲得某一時(shí)刻內(nèi)存的使用情況,這樣我們很好地定位內(nèi)存問(wèn)題拱层,是每個(gè)開(kāi)發(fā)者必須要掌握的工具弥臼!


更多文章,歡迎訪問(wèn)我的 Android 知識(shí)梳理系列:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末根灯,一起剝皮案震驚了整個(gè)濱河市径缅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烙肺,老刑警劉巖纳猪,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異桃笙,居然都是意外死亡氏堤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門搏明,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鼠锈,“玉大人闪檬,你說(shuō)我怎么就攤上這事」喊剩” “怎么了谬以?”我有些...
    開(kāi)封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)由桌。 經(jīng)常有香客問(wèn)我为黎,道長(zhǎng),這世上最難降的妖魔是什么行您? 我笑而不...
    開(kāi)封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任铭乾,我火速辦了婚禮,結(jié)果婚禮上娃循,老公的妹妹穿的比我還像新娘炕檩。我一直安慰自己,他們只是感情好捌斧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布笛质。 她就那樣靜靜地躺著,像睡著了一般捞蚂。 火紅的嫁衣襯著肌膚如雪妇押。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天姓迅,我揣著相機(jī)與錄音敲霍,去河邊找鬼。 笑死丁存,一個(gè)胖子當(dāng)著我的面吹牛肩杈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播解寝,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼扩然,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了聋伦?” 一聲冷哼從身側(cè)響起夫偶,我...
    開(kāi)封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嘉抓,沒(méi)想到半個(gè)月后索守,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晕窑,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抑片,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杨赤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敞斋。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡截汪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出植捎,到底是詐尸還是另有隱情衙解,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布焰枢,位于F島的核電站蚓峦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏济锄。R本人自食惡果不足惜暑椰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荐绝。 院中可真熱鬧一汽,春花似錦、人聲如沸低滩。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)匈勋。三九已至绞呈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間婶溯,已是汗流浹背枫虏。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爬虱,地道東北人隶债。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像跑筝,于是被迫代替她去往敵國(guó)和親死讹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容