
日常開發(fā)中,我們經(jīng)常通過(guò)Studio自帶的Menmery Monitor來(lái)查看、分析App的內(nèi)存占用問(wèn)題。但是在遇到復(fù)雜的內(nèi)存泄漏問(wèn)題時(shí),我們就需要通過(guò)Memory Analyzer Tool來(lái)分析問(wèn)題了。相對(duì)來(lái)說(shuō),Memory Analyzer Tool 比 Memory Monitor要強(qiáng)大一些,(MAT下載:http://eclipse.org/mat/downloads.php)
下面通過(guò)一個(gè)的demo來(lái)介紹MAT工具的使用。
先簡(jiǎn)單說(shuō)下我們的demo,本demo中通過(guò)ScrollingMainActivity中的一個(gè)按鈕打開PropertyActivity,然后back返回到ScrollingMainActivity。
??看看有內(nèi)存泄漏的PropertyActivity,上代碼:
public class PropertyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_property);
initViews();
}
private void initViews() {
initTestMemoryMonitor();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
private void initTestMemoryMonitor() {
DataManger userManger = DataManger.getInstance(this);
}
}
這段代碼是單例場(chǎng)景典型的內(nèi)存泄漏問(wèn)題,即DataManger的實(shí)例一直持有PropertyActivity的實(shí)例,導(dǎo)致用戶返回時(shí)PropertyActivity的實(shí)例無(wú)法被回收。那么這樣的問(wèn)題我們?cè)趺赐ㄟ^(guò)MAT分析判斷呢。
第一步、生成需要的標(biāo)準(zhǔn)hprof文件(兩個(gè):一個(gè)有泄漏問(wèn)題的,一個(gè)沒(méi)泄漏問(wèn)題的)
因?yàn)檫@次我們內(nèi)存泄漏的疑點(diǎn)在PropertyActivity,所以我們可以在打開PropertyActivity頁(yè)面時(shí)生成一個(gè)無(wú)泄漏問(wèn)題的hprof文件(test1.hprof)。然后在打開PropertyActivity頁(yè)面后back返回到ScrollingMainActivity頁(yè)面后再生成一個(gè)有泄漏問(wèn)題的hprof文件(test2.hprof)。主要步驟可以參考下圖。
注意!在生成文件前 先主動(dòng)Gc下(排除系統(tǒng)Gc不及時(shí)的影響)。


第二步、導(dǎo)入兩個(gè)分析文件
現(xiàn)在我們打開MAT軟件,通過(guò)軟件的 File -> Open File ->選中兩個(gè)文件。導(dǎo)入文件后的軟件界面如下:

在我們具體分析文件前,先說(shuō)明下兩個(gè)名詞:
Shallow Heap
Shallow Heap size就是對(duì)象本身占用內(nèi)存的大小,不包含其引用的對(duì)象內(nèi)存,可以作為參考數(shù)據(jù)。
Retained Heap
Retained Heap size是該對(duì)象自己的Shallow size,加上從該對(duì)象能直接或間接訪問(wèn)到對(duì)象的Shallow size之和。你也可以理解為,Retained size是該對(duì)象被GC之后所能回收到內(nèi)存的總和。Retained Heap可以更精確的反映一個(gè)對(duì)象實(shí)際占用的大?。ㄒ?yàn)槿绻搶?duì)象釋放,Retained heap都可以被釋放)。
使用MAT工具分析內(nèi)存泄漏一般分為三種:直方圖、支配樹、對(duì)比法。個(gè)人用推薦用直方圖和對(duì)比法去分析問(wèn)題,后面會(huì)重點(diǎn)分析這兩種方法:
1、直方圖(Histogram)

test2.hprof的數(shù)據(jù),在這里可以看到列出的每一個(gè)類的名字、實(shí)例數(shù)和大小。
我們可以看到當(dāng)前總共有4719個(gè)類,默認(rèn)是通過(guò)class(group by class)分類展示的。
其中我們可以看到byte[]、char[]這些對(duì)象的Shallow Heap最高,說(shuō)明我們應(yīng)用程序中用了很多byte[]類型的數(shù)據(jù)。我們也可以通過(guò)右鍵 -> List objects -> with incoming references來(lái)查看具體是誰(shuí)在使用這些byte[]。
回到正題,為了在這么多數(shù)據(jù)中快速找到我們的疑點(diǎn)對(duì)象,我們需要使用檢索(支持正則表達(dá)式)。因?yàn)槲覀儜岩傻膶?duì)象是Activity有內(nèi)存泄漏,下面就檢索區(qū)域里輸入Activity(軟件為默認(rèn)設(shè)定成.*Activity.* 你可以自行改成.*Activity)效果如下圖。

在這里我們可以看到PropertyActivity的實(shí)例數(shù)量盡然是 1 !!!,這就是內(nèi)存泄漏所在。那么 為什么會(huì)有這個(gè)內(nèi)存泄漏呢?
這就需要查看PropertyActivity被誰(shuí)引用了,不能被釋放。我們?cè)赑ropertyActivity上右鍵選擇exclude all phantom/weak/soft etc.references, 意思是查看排除虛引用/弱引用/軟引用等的引用鏈 (這些引用最終都能夠被GC干掉,所以排除)


好啦。到這里我們已經(jīng)通過(guò)直方圖的方法分析內(nèi)存泄漏的原因。但是實(shí)際工作中,往往無(wú)法準(zhǔn)確的判斷出疑似目標(biāo)。因此,為了快速的定位出疑似問(wèn)題點(diǎn),我們可以通過(guò)對(duì)比法來(lái)找出目標(biāo)。(這也是為什么保存兩份快照文件的原因了),下面我們用對(duì)比法來(lái)定位目標(biāo):
2、對(duì)比法


除此之外,我們還可以對(duì)比多個(gè)hprof文件來(lái)分析數(shù)據(jù),具體如下步驟:


3、補(bǔ)充
對(duì)于熟悉SQL的同學(xué)也可以利用OQL來(lái)查詢

至此,這大概就是MAT工具最常用的一些用法了,用法就說(shuō)到這里,具體還需大家在實(shí)際工作中體會(huì)。當(dāng)然 MAT工具也沒(méi)有辦法保證一定可以將內(nèi)存泄漏的原因找出來(lái),還是需要我們對(duì)程序的代碼有足夠多的了解,知道有哪些對(duì)象是存活的,以及它們存活的原因,然后在合適的時(shí)間點(diǎn)生成快照文件,最后再結(jié)合MAT給出的數(shù)據(jù)來(lái)進(jìn)行具體的分析,這樣才有可能把一些隱藏得很深的問(wèn)題原因給找出來(lái)。
【原創(chuàng)出品 未經(jīng)授權(quán) 禁止轉(zhuǎn)載】
【歡迎微友分享轉(zhuǎn)發(fā) 禁止公號(hào)等未經(jīng)授權(quán)的轉(zhuǎn)載】