Android內(nèi)存優(yōu)化(四)解析Memory Monitor嫁盲、Allocation Tracker和Heap Dump

前言

要想做好內(nèi)存優(yōu)化工作叛本,就要掌握兩大部分的知識(shí)沪蓬,一部分是知道并理解內(nèi)存優(yōu)化相關(guān)的原理,另一部分就是善于運(yùn)用內(nèi)存分析的工具来候。本篇就來(lái)介紹內(nèi)存分析工具:Memory Monitor跷叉、Allocation Tracker和Heap Dump的使用方法。

1.Memory Monitor

在Android Studio(以下簡(jiǎn)稱AS)中Android Monitor是一個(gè)主窗口营搅,它包含了Logcat,云挟、Memory Monitor、CPU Monitor转质、 GPU Monitor和Network Monitor园欣。其中Memory Monitor可以輕松地監(jiān)視應(yīng)用程序的性能和內(nèi)存使用情況,以便于找到被分配的對(duì)象休蟹,定位內(nèi)存泄漏沸枯,并跟蹤連接設(shè)備中正在使用的內(nèi)存數(shù)量日矫。Memory Monitor可以報(bào)告出你的應(yīng)用程序的內(nèi)存分配情況, 更形象的呈現(xiàn)出應(yīng)用程序使用的內(nèi)存绑榴。它的作用如下:

  • 實(shí)時(shí)顯示可用的和分配的Java內(nèi)存的圖表哪轿。
  • 實(shí)時(shí)顯示垃圾收集(GC)事件。
  • 啟動(dòng)垃圾收集事件翔怎。
  • 快速測(cè)試應(yīng)用程序的緩慢是否與過(guò)度的垃圾收集事件有關(guān)窃诉。
  • 快速測(cè)試應(yīng)用程序崩潰是否與內(nèi)存耗盡有關(guān)。

1.1 使用Memory Monitor

在使用Memory Monitor之前要確保手機(jī)開啟了開發(fā)者模式和USB調(diào)試赤套。
使用的步驟為:
1.運(yùn)行需要監(jiān)控的應(yīng)用程序飘痛。
2.點(diǎn)擊AS面板下面的Android圖標(biāo),并選擇Monitors選項(xiàng)容握。
如果Memory Monitor已經(jīng)運(yùn)行敦冬,效果如下圖所示(AS版本2.3.2)。


圖中的標(biāo)注的功能如下:

  • Initiate GC(標(biāo)識(shí)1):用來(lái)手動(dòng)觸發(fā)GC唯沮。
  • Dump Java heap(標(biāo)識(shí)2):保存內(nèi)存快照脖旱。
  • Start/Stop Allocation Tracking(標(biāo)識(shí)3):打開Allocation Tracker工具(后面會(huì)介紹)。
  • Free(標(biāo)識(shí)4):當(dāng)前應(yīng)用未分配的內(nèi)存大小介蛉。
  • Allocated(標(biāo)識(shí)5):當(dāng)前應(yīng)用分配的內(nèi)存大小萌庆。

圖中y軸顯示當(dāng)前應(yīng)用的分配的內(nèi)存和未分配的內(nèi)存大小币旧;x軸表示經(jīng)過(guò)的時(shí)間践险。

1.2 大內(nèi)存申請(qǐng)與GC

am-gc2.png

從上圖可以看出,分配的內(nèi)存急劇上升吹菱,這就是大內(nèi)存分配的場(chǎng)景巍虫,我們要判斷這是否是合理的分配的內(nèi)存,是Bitmap還是其他的大數(shù)據(jù)鳍刷,并且對(duì)這種大數(shù)據(jù)進(jìn)行優(yōu)化占遥,減少內(nèi)存開銷。
接下來(lái)分配的內(nèi)存出現(xiàn)急劇下降输瓜,這表示垃圾收集事件瓦胎,用來(lái)釋放內(nèi)存。

1.3 內(nèi)存抖動(dòng)

內(nèi)存抖動(dòng)一般指在很短的時(shí)間內(nèi)發(fā)生了多次內(nèi)存分配和釋放尤揣,嚴(yán)重的內(nèi)存抖動(dòng)還會(huì)導(dǎo)致應(yīng)用程序卡頓搔啊。內(nèi)存抖動(dòng)出現(xiàn)原因主要是短時(shí)間頻繁的創(chuàng)建對(duì)象(可能在循環(huán)中創(chuàng)建對(duì)象),內(nèi)存為了應(yīng)對(duì)這種情況北戏,也會(huì)頻繁的進(jìn)行GC负芋,因此綜合起來(lái)就產(chǎn)生了內(nèi)存抖動(dòng),產(chǎn)生了如上圖般的鋸齒狀嗜愈。

2.Allocation Tracker

Allocation Tracker用來(lái)跟蹤內(nèi)存分配旧蛾,它允許你在執(zhí)行某些操作的同時(shí)監(jiān)視在何處分配對(duì)象惩猫,了解這些分配使你能夠調(diào)整與這些操作相關(guān)的方法調(diào)用,以優(yōu)化應(yīng)用程序性能和內(nèi)存使用蚜点。
Allocation Tracker能夠做到如下的事情:

  • 顯示代碼分配對(duì)象類型、大小拌阴、分配線程和堆棧跟蹤的時(shí)間和位置绍绘。
  • 通過(guò)重復(fù)的分配/釋放模式幫助識(shí)別內(nèi)存變化。
  • 當(dāng)與 HPROF Viewer結(jié)合使用時(shí)迟赃,可以幫助你跟蹤內(nèi)存泄漏陪拘。例如,如果你在堆上看到一個(gè)bitmap對(duì)象纤壁,你可以使用Allocation Tracker來(lái)找到其分配的位置左刽。

2.1 使用Allocation Tracker

AS和DDMS中都有Allocation Tracker,這里會(huì)·介紹AS中的Allocation Tracke如何使用酌媒。首先要確保要確保手機(jī)開啟了開發(fā)者模式欠痴,并且開啟了USB調(diào)試。
使用的步驟為:
1.運(yùn)行需要監(jiān)控的應(yīng)用程序秒咨。
2.點(diǎn)擊AS面板下面的Android圖標(biāo)喇辽,并選擇Monitors選項(xiàng)。
3.點(diǎn)擊Start Allocation Tracking按鈕雨席,這時(shí)Start Allocation Tracking按鈕變?yōu)榱薙top Allocation Tracking按鈕菩咨。
4.操作應(yīng)用程序。
5.點(diǎn)擊Stop Allocation Tracking按鈕陡厘,結(jié)束快照抽米。這時(shí)Memory Monitor會(huì)顯示出捕獲快照的期間,如下圖所示糙置。


6.過(guò)幾秒后就會(huì)自動(dòng)打開一個(gè)窗口云茸,顯示當(dāng)前生成的alloc文件的內(nèi)存數(shù)據(jù)。

2.2 alloc文件分析

自動(dòng)打開的alloc文件窗口如下圖所示谤饭。


該alloc文件顯示以下信息:

| 列 | 說(shuō)明|
| :-------- :|: --------:|
| Method | 負(fù)責(zé)分配的Java方法|
| Count| 分配的實(shí)例總數(shù)|
| Total Size| 分配內(nèi)存的總字節(jié)數(shù)|

接著我們來(lái)分析標(biāo)紅框的內(nèi)容查辩,負(fù)責(zé)分配的Java方法為performLaunchActivity,內(nèi)存分配序列為2369网持,分配的對(duì)象為ActivityThread宜岛,分配的實(shí)例總數(shù)為300個(gè),分配內(nèi)存的總字節(jié)數(shù)為10512功舀。不了解performLaunchActivity方法和ActivityThread可以看Android深入四大組件這一系列的文章萍倡。

目前的菜單選項(xiàng)是Group by Method我們也可以選擇 Group By Allocator,如下圖所示辟汰。


為了更好的解釋圖中的信息列敲,這里給出測(cè)試的代碼阱佛,MainActivity和SecondActivity 的代碼如下所示。
MainActivity.java

public class MainActivity extends AppCompatActivity {
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button =(Button)findViewById(R.id.bt_next);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this,SecondActivity.class));
            }
        });
    }
}

SecondActivity.java

public class SecondActivity extends AppCompatActivity {
    private static Object inner;
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.bt_next);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                createInnerClass();
                finish();
            }
        });
    }
    void createInnerClass() {
        class InnerClass {
        }
        inner = new InnerClass();
    }
}

其中SecondActivity是存在內(nèi)存泄漏的戴而,生成快照期間凑术,我的操作就是在MainActivity和SecondActivity跳轉(zhuǎn)了3次(點(diǎn)擊button 共6次)。這時(shí)我們回過(guò)頭來(lái)看上圖的紅框的信息所意,MainActivity總共分配了3個(gè)Intent實(shí)例淮逊,占用內(nèi)存為192字節(jié)。SecondActivity總共分配了6個(gè)實(shí)例扶踊,占用內(nèi)存為96字節(jié)泄鹏,其中分配了3個(gè)匿名內(nèi)部類OnClickListener的實(shí)例,3個(gè)InnerClass的實(shí)例秧耗。

我們可以選擇列表中的一項(xiàng)备籽,單擊鼠標(biāo)右鍵,在彈出的菜單中選擇jump to the source就可以跳轉(zhuǎn)到對(duì)應(yīng)的源文件中分井。
除此之外车猬,還可以點(diǎn)擊Show/Hide Chart按鈕來(lái)顯示數(shù)據(jù)的圖形化,如下圖所示尺锚。


3.Heap Dump

Heap Dump的主要功能就是查看不同的數(shù)據(jù)類型在內(nèi)存中的使用情況诈唬。它可以幫助你找到大對(duì)象,也可以通過(guò)數(shù)據(jù)的變化發(fā)現(xiàn)內(nèi)存泄漏缩麸。

3.1 使用Heap Dump

打開Android Device Monitor工具铸磅,在左邊Devices列表中選擇要查看的應(yīng)用程序進(jìn)程,點(diǎn)擊Update Heap按鈕(裝有一半綠色液體的圓柱體)杭朱,在右邊選擇Heap選項(xiàng)阅仔,并點(diǎn)擊Cause GC按鈕,就會(huì)開始顯示數(shù)據(jù)弧械。我們每次點(diǎn)擊Cause GC按鈕都會(huì)強(qiáng)制應(yīng)用程序進(jìn)行垃圾回收八酒,并將清理后的數(shù)據(jù)顯示在Heap工具中。如下圖所示刃唐。

從上圖可以看出羞迷,Heap工具共有三個(gè)區(qū)域,分別是總覽視圖(標(biāo)識(shí)1)画饥、詳情視圖(標(biāo)識(shí)2)和內(nèi)存分配柱狀圖(標(biāo)識(shí)2)衔瓮。

3.2 總覽視圖

其中總覽視圖可以查看整體的內(nèi)存情況,表中的顯示信息如下所示抖甘。

|列 | 說(shuō)明 |
| :-------- :|: --------:|
| Heap Size|堆棧分配給該應(yīng)用程序的內(nèi)存大小 |
| Allocated|已分配使用的內(nèi)存大小 |
| Free|空閑的內(nèi)存大小 |
| %Used|當(dāng)前Heap的使用率(Allocated/Heap Size) |
| #Objects|對(duì)象的數(shù)量 |

結(jié)合上表和上圖热鞍,我們?cè)诳傆[視圖獲得的信息就是:堆棧分配給當(dāng)前的應(yīng)用程序的內(nèi)存大小為2.346MB,已分配的內(nèi)存為1.346MB,空閑的內(nèi)存為1MB薇宠,當(dāng)前Heap的使用率為57.37%偷办,對(duì)象的數(shù)量為24058個(gè)。

3.3 詳情視圖

詳細(xì)視圖展示了所有的數(shù)據(jù)類型的內(nèi)存情況澄港,表中列的信息如下所示椒涯。

|列 | 說(shuō)明 |
| :-------- :|: --------:|
| Type|數(shù)據(jù)類型 |
| Total Size|總共占用的內(nèi)存大小 |
| Smallest|將該數(shù)據(jù)類型的對(duì)象從小到大排列,排在第一個(gè)的對(duì)象所占用的內(nèi)存 |
| Largest|將該數(shù)據(jù)類型的對(duì)象從小到大排列回梧,排在最后一個(gè)的對(duì)象所占用的內(nèi)存 |
| Median|將該數(shù)據(jù)類型的對(duì)象從小到大排列废岂,排在中間的對(duì)象所占用的內(nèi)存 |
| Average|該數(shù)據(jù)類型的對(duì)象所占用內(nèi)存的平均值 |

除了列的信息,還有行信息:

|行 | 說(shuō)明 |
| :-------- :|: --------:|
| free|內(nèi)存碎片|
| data object|對(duì)象 |
| class object|類 |
| 1-byte array (byte[],boolean[])|1字節(jié)的數(shù)組對(duì)象 |
| 2-byte array (short[],char[])|2字節(jié)的數(shù)組對(duì)象 |
| 4-byte array (object[],int[],float[])|4字節(jié)的數(shù)組對(duì)象 |
| 6-byte array (long[],double[])|8字節(jié)的數(shù)組對(duì)象 |
| non-Java object|非Java對(duì)象 |

行信息中比較重要的是free漂辐,它與總覽視圖中的free的含義不同,它代表內(nèi)存碎片棕硫。當(dāng)新創(chuàng)建一個(gè)對(duì)象時(shí)髓涯,如果碎片內(nèi)存能容下該對(duì)象,則復(fù)用碎片內(nèi)存哈扮,否則就會(huì)從free空間(總覽視圖中的free)重新劃分內(nèi)存給這個(gè)新對(duì)象纬纪。free是判斷內(nèi)存碎片化程度的一個(gè)重要的指標(biāo)。
此外滑肉,1-byte array這一行的信息也很重要包各,因?yàn)閳D片是以byte[]的形式存儲(chǔ)在內(nèi)存中的,如果1-byte array一行的數(shù)據(jù)過(guò)大靶庙,則需要檢查圖片的內(nèi)存管理了问畅。

3.4 檢測(cè)內(nèi)存泄漏

Heap Dump也可以檢測(cè)內(nèi)存泄漏。在左邊Devices列表中選擇要查看的應(yīng)用程序進(jìn)程六荒,點(diǎn)擊Update Heap按鈕(裝有一半綠色液體的圓柱體)护姆,在右邊選擇Heap選項(xiàng),并點(diǎn)擊Cause GC按鈕掏击,就會(huì)開始顯示數(shù)據(jù)卵皂,如下圖所示。

這時(shí)data object的Total Size為270.266KB砚亭。接下來(lái)操作應(yīng)用灯变,這個(gè)應(yīng)用仍舊是在2.2小節(jié)所舉的內(nèi)存泄漏的例子,我反復(fù)的在MainActivity和SecondActivity跳轉(zhuǎn)了10次(點(diǎn)擊Button共20次)捅膘,數(shù)據(jù)顯示為:


data object的Total Size變?yōu)榱?68.172KB添祸。這時(shí)我點(diǎn)擊Cause GC按鈕,數(shù)據(jù)顯示為:

可以看到data object的Total Size變?yōu)榱?44.516KB寻仗,再點(diǎn)擊一次Cause GC按鈕:


Total Size變?yōu)榱?23.312KB膝捞,經(jīng)過(guò)兩次Cause GC的操作,Total Size的值從768.172KB變?yōu)榱?23.312KB,這是一個(gè)比較大的變化蔬咬,說(shuō)明在Cause GC操作之前有462.86KB(768.172KB-323.312KB)的內(nèi)存沒(méi)有被回收鲤遥,可能發(fā)生了內(nèi)存泄漏。

參考資料
Memory Monitor
Allocation Tracker
Android Monitor Basics
Android性能專項(xiàng)測(cè)試之Memory Monitor工具
《Android應(yīng)用性能優(yōu)化最佳實(shí)踐》
《Android群英傳 神兵利器》
《高性能Android應(yīng)用開發(fā)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末林艘,一起剝皮案震驚了整個(gè)濱河市盖奈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狐援,老刑警劉巖钢坦,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異啥酱,居然都是意外死亡爹凹,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門镶殷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)禾酱,“玉大人,你說(shuō)我怎么就攤上這事绘趋〔眨” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵陷遮,是天一觀的道長(zhǎng)滓走。 經(jīng)常有香客問(wèn)我,道長(zhǎng)帽馋,這世上最難降的妖魔是什么搅方? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮绽族,結(jié)果婚禮上腰懂,老公的妹妹穿的比我還像新娘。我一直安慰自己项秉,他們只是感情好绣溜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著娄蔼,像睡著了一般怖喻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上岁诉,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天锚沸,我揣著相機(jī)與錄音,去河邊找鬼涕癣。 笑死哗蜈,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播距潘,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼炼列,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了音比?” 一聲冷哼從身側(cè)響起俭尖,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洞翩,沒(méi)想到半個(gè)月后稽犁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骚亿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年已亥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片来屠。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虑椎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出的妖,到底是詐尸還是另有隱情绣檬,我是刑警寧澤足陨,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布嫂粟,位于F島的核電站,受9級(jí)特大地震影響墨缘,放射性物質(zhì)發(fā)生泄漏星虹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一镊讼、第九天 我趴在偏房一處隱蔽的房頂上張望宽涌。 院中可真熱鬧,春花似錦蝶棋、人聲如沸卸亮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)兼贸。三九已至,卻和暖如春吃溅,著一層夾襖步出監(jiān)牢的瞬間溶诞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工决侈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留螺垢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像枉圃,于是被迫代替她去往敵國(guó)和親功茴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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