MAT使用--转
原文地址:
【1】http://ju.outofmemory.cn/entry/172684
【2】http://ju.outofmemory.cn/entry/129445
MAT使用入門
MAT簡(jiǎn)介
MAT介紹
MAT(Memory Analyzer Tool),一個(gè)基于Eclipse的內(nèi)存分析工具,是一個(gè)快速、功能豐富的JAVA heap分析工具,它可以幫助我們查找內(nèi)存泄漏和減少內(nèi)存消耗。使用內(nèi)存分析工具從眾多的對(duì)象中進(jìn)行分析,快速的計(jì)算出在內(nèi)存中對(duì)象的占用大小,看看是誰(shuí)阻止了垃圾收集器的回收工作,并可以通過(guò)報(bào)表直觀的查看到可能造成這種結(jié)果的對(duì)象。
當(dāng)然MAT也有獨(dú)立的不依賴Eclipse的版本,只不過(guò)這個(gè)版本在調(diào)試Android內(nèi)存的時(shí)候,需要將DDMS生成的文件進(jìn)行轉(zhuǎn)換,才可以在獨(dú)立版本的MAT上打開(kāi)。不過(guò)Android SDK中已經(jīng)提供了這個(gè)Tools,所以使用起來(lái)也是很方便的。
MAT工具的下載安裝
這里是MAT的下載地址:?https://eclipse.org/mat/downloads.php?,下載時(shí)會(huì)提供三種選擇的方式:
- Update Site 這種方式后面會(huì)有一個(gè)網(wǎng)址:比如?http://download.eclipse.org/mat/1.4/update-site/?,安裝過(guò)Eclipse插件的同學(xué)應(yīng)該知道,只要把這段網(wǎng)址復(fù)制到對(duì)應(yīng)的Eclipse的Install New Software那里,就可以進(jìn)行在線下載了。
- Archived Update Site 這種方式安裝的位置和上一種差不多,只不過(guò)第一種是在線下載,這一種是使用離線包進(jìn)行更新,這種方式劣勢(shì)是當(dāng)這個(gè)插件更新后,需要重新下載離線包,而第一種方式則可以在線下載更新。
- Stand-alone Eclipse RCP Applications 這種方式就是把MAT當(dāng)成一個(gè)獨(dú)立的工具使用,不再依附于Eclipse,適合不使用Eclipse而使用Android Studio的同學(xué)。這種方式有個(gè)麻煩的地方就是DDMS導(dǎo)出的文件,需要進(jìn)行轉(zhuǎn)換才可以在MAT中打開(kāi)。
下載安裝好之后,就可以使用MAT進(jìn)行實(shí)際的操作了。
Android(Java)中常見(jiàn)的容易引起內(nèi)存泄露的不良代碼
Android內(nèi)存
使用MAT工具之前,要對(duì)Android的內(nèi)存分配方式有基本的了解,對(duì)容易引起內(nèi)存泄露的代碼也要保持敏感,在代碼級(jí)別對(duì)內(nèi)存泄露的排查,有助于內(nèi)存的使用。
Android主要應(yīng)用在嵌入式設(shè)備當(dāng)中,而嵌入式設(shè)備由于一些眾所周知的條件限制,通常都不會(huì)有很高的配置,特別是內(nèi)存是比較有限的。如果我們編寫的代碼當(dāng)中有太多的對(duì)內(nèi)存使用不當(dāng)?shù)牡胤?#xff0c;難免會(huì)使得我們的設(shè)備運(yùn)行緩慢,甚至是死機(jī)。為了能夠使得Android應(yīng)用程序安全且快速的運(yùn)行,Android的每個(gè)應(yīng)用程序都會(huì)使用一個(gè)專有的Dalvik虛擬機(jī)實(shí)例來(lái)運(yùn)行,它是由Zygote服務(wù)進(jìn)程孵化出來(lái)的,也就是說(shuō)每個(gè)應(yīng)用程序都是在屬于自己的進(jìn)程中運(yùn)行的。一方面,如果程序在運(yùn)行過(guò)程中出現(xiàn)了內(nèi)存泄漏的問(wèn)題,僅僅會(huì)使得自己的進(jìn)程被kill掉,而不會(huì)影響其他進(jìn)程(如果是system_process等系統(tǒng)進(jìn)程出問(wèn)題的話,則會(huì)引起系統(tǒng)重啟)。另一方面Android為不同類型的進(jìn)程分配了不同的內(nèi)存使用上限,如果應(yīng)用進(jìn)程使用的內(nèi)存超過(guò)了這個(gè)上限,則會(huì)被系統(tǒng)視為內(nèi)存泄漏,從而被kill掉。
常見(jiàn)的內(nèi)存使用不當(dāng)?shù)那闆r
查詢數(shù)據(jù)庫(kù)沒(méi)有關(guān)閉游標(biāo)
描述:?
程序中經(jīng)常會(huì)進(jìn)行查詢數(shù)據(jù)庫(kù)的操作,但是經(jīng)常會(huì)有使用完畢Cursor后沒(méi)有關(guān)閉的情況。如果我們的查詢結(jié)果集比較小,對(duì)內(nèi)存的消耗不容易被發(fā)現(xiàn),只有在常時(shí)間大量操作的情況下才會(huì)復(fù)現(xiàn)內(nèi)存問(wèn)題,這樣就會(huì)給以后的測(cè)試和問(wèn)題排查帶來(lái)困難和風(fēng)險(xiǎn)。?
示例代碼:
if (cursor.moveToNext()) {
... ...
}
修正示例代碼:
Cursor cursor = null;try {
cursor = getContentResolver().query(uri ...);
if (cursor != null && cursor.moveToNext()) {
... ...
}
} finally {
if (cursor != null) {
try {
cursor.close();
} catch (Exception e) {
//ignore this
}
}
}
`
構(gòu)造Adapter時(shí),沒(méi)有使用緩存的 convertView
描述:以構(gòu)造ListView的BaseAdapter為例,在BaseAdapter中提供了方法:
public View getView(int position, View convertView, ViewGroup parent)來(lái)向ListView提供每一個(gè)item所需要的view對(duì)象。初始時(shí)ListView會(huì)從BaseAdapter中根據(jù)當(dāng)前的屏幕布局實(shí)例化一定數(shù)量的view對(duì)象,同時(shí)ListView會(huì)將這些view對(duì)象緩存起來(lái)。當(dāng)向上滾動(dòng)ListView時(shí),原先位于最上面的list item的view對(duì)象會(huì)被回收,然后被用來(lái)構(gòu)造新出現(xiàn)的最下面的list item。這個(gè)構(gòu)造過(guò)程就是由getView()方法完成的,getView()的第二個(gè)形參 View convertView就是被緩存起來(lái)的list item的view對(duì)象(初始化時(shí)緩存中沒(méi)有view對(duì)象則convertView是null)。?
由此可以看出,如果我們不去使用convertView,而是每次都在getView()中重新實(shí)例化一個(gè)View對(duì)象的話,即浪費(fèi)資源也浪費(fèi)時(shí)間,也會(huì)使得內(nèi)存占用越來(lái)越大。ListView回收l(shuí)ist item的view對(duì)象的過(guò)程可以查看:android.widget.AbsListView.java —> void addScrapView(View scrap) 方法。
示例代碼:
public View getView(int position, View convertView, ViewGroup parent) {View view = new Xxx(...);
... ...
return view;
}
`?
示例修正代碼:
View view = null;
if (convertView != null) {
view = convertView;
populate(view, getItem(position));
...
} else {
view = new Xxx(...);
...
}
return view;
}
關(guān)于ListView的使用和優(yōu)化,可以參考這兩篇文章:
- Using lists in Android (ListView) - Tutorial?
]() - Making ListView Scrolling Smooth
Bitmap對(duì)象不在使用時(shí)調(diào)用recycle()釋放內(nèi)存
描述:有時(shí)我們會(huì)手工的操作Bitmap對(duì)象,如果一個(gè)Bitmap對(duì)象比較占內(nèi)存,當(dāng)它不在被使用的時(shí)候,可以調(diào)用Bitmap.recycle()方法回收此對(duì)象的像素所占用的內(nèi)存。?
另外在最新版本的Android開(kāi)發(fā)時(shí),使用下面的方法也可以釋放此Bitmap所占用的內(nèi)存
...
bitmap初始化以及使用
...
bitmap = null;
釋放對(duì)象的引用
描述:這種情況描述起來(lái)比較麻煩,舉兩個(gè)例子進(jìn)行說(shuō)明。
示例A:?
假設(shè)有如下操作
... ...
private Handler mHandler = ...
private Object obj;
public void operation() {
obj = initObj();
...
[Mark]
mHandler.post(new Runnable() {
public void run() {
useObj(obj);
}
});
}
}
我們有一個(gè)成員變量 obj,在operation()中我們希望能夠?qū)⑻幚韔bj實(shí)例的操作post到某個(gè)線程的MessageQueue中。在以上的代碼中,即便是mHandler所在的線程使用完了obj所引用的對(duì)象,但這個(gè)對(duì)象仍然不會(huì)被垃圾回收掉,因?yàn)镈emoActivity.obj還保有這個(gè)對(duì)象的引用。所以如果在DemoActivity中不再使用這個(gè)對(duì)象了,可以在[Mark]的位置釋放對(duì)象的引用,而代碼可以修改為:
public void operation() {obj = initObj();
...
final Object o = obj;
obj = null;
mHandler.post(new Runnable() {
public void run() {
useObj(o);
}
}
}
示例B:?
假設(shè)我們希望在鎖屏界面(LockScreen)中,監(jiān)聽(tīng)系統(tǒng)中的電話服務(wù)以獲取一些信息(如信號(hào)強(qiáng)度等),則可以在LockScreen中定義一個(gè)PhoneStateListener的對(duì)象,同時(shí)將它注冊(cè)到TelephonyManager服務(wù)中。對(duì)于LockScreen對(duì)象,當(dāng)需要顯示鎖屏界面的時(shí)候就會(huì)創(chuàng)建一個(gè)LockScreen對(duì)象,而當(dāng)鎖屏界面消失的時(shí)候LockScreen對(duì)象就會(huì)被釋放掉。
但是如果在釋放LockScreen對(duì)象的時(shí)候忘記取消我們之前注冊(cè)的PhoneStateListener對(duì)象,則會(huì)導(dǎo)致LockScreen無(wú)法被垃圾回收。如果不斷的使鎖屏界面顯示和消失,則最終會(huì)由于大量的LockScreen對(duì)象沒(méi)有辦法被回收而引起OutOfMemory,使得system_process進(jìn)程掛掉。
總之當(dāng)一個(gè)生命周期較短的對(duì)象A,被一個(gè)生命周期較長(zhǎng)的對(duì)象B保有其引用的情況下,在A的生命周期結(jié)束時(shí),要在B中清除掉對(duì)A的引用。
其他
Android應(yīng)用程序中最典型的需要注意釋放資源的情況是在Activity的生命周期中,在onPause()、onStop()、onDestroy()方法中需要適當(dāng)?shù)尼尫刨Y源的情況。由于此情況很基礎(chǔ),在此不詳細(xì)說(shuō)明,具體可以查看官方文檔對(duì)Activity生命周期的介紹,以明確何時(shí)應(yīng)該釋放哪些資源。
另外一些其他的例子,將會(huì)在補(bǔ)充版本加入。
使用MAT進(jìn)行內(nèi)存調(diào)試
獲取HPROF文件
HPROF文件是MAT能識(shí)別的文件,HPROF文件存儲(chǔ)的是特定時(shí)間點(diǎn),java進(jìn)程的內(nèi)存快照。有不同的格式來(lái)存儲(chǔ)這些數(shù)據(jù),總的來(lái)說(shuō)包含了快照被觸發(fā)時(shí)java對(duì)象和類在heap中的情況。由于快照只是一瞬間的事情,所以heap dump中無(wú)法包含一個(gè)對(duì)象在何時(shí)、何地(哪個(gè)方法中)被分配這樣的信息。?
這個(gè)文件可以使用DDMS導(dǎo)出:
DDMS中在Devices上面有一排按鈕,選擇一個(gè)進(jìn)程后(即在Devices下面列出的列表中選擇你要調(diào)試的應(yīng)用程序的包名),點(diǎn)擊Dump HPROF file 按鈕:
選擇存儲(chǔ)路徑保存后就可以得到對(duì)應(yīng)進(jìn)程的HPROF文件。eclipse插件可以把上面的工作一鍵完成。只需要點(diǎn)擊Dump HPROF file圖標(biāo),然后MAT插件就會(huì)自動(dòng)轉(zhuǎn)換格式,并且在eclipse中打開(kāi)分析結(jié)果。eclipse中還專門有個(gè)Memory Analysis視圖
轉(zhuǎn)換過(guò)后的.hprof文件即可使用MAT工具打開(kāi)了。
MAT主界面介紹
這里介紹的不是MAT這個(gè)工具的主界面,而是導(dǎo)入一個(gè)文件之后,顯示OverView的界面。
- 打開(kāi)經(jīng)過(guò)轉(zhuǎn)換的hprof文件:?
如果選擇了第一個(gè),則會(huì)生成一個(gè)報(bào)告。這個(gè)無(wú)大礙。
-
選擇OverView界面:
我們需要關(guān)注的是下面的Actions區(qū)域
-
Histogram:列出內(nèi)存中的對(duì)象,對(duì)象的個(gè)數(shù)以及大小
-
Dominator Tree:列出最大的對(duì)象以及其依賴存活的Object (大小是以Retained Heap為標(biāo)準(zhǔn)排序的)
-
Top Consumers : 通過(guò)圖形列出最大的object
-
Duplicate Class:通過(guò)MAT自動(dòng)分析泄漏的原因
一般Histogram和 Dominator Tree是最常用的。
MAT中一些概念介紹
要看懂MAT的列表信息,Shallow heap、Retained Heap、GC Root這幾個(gè)概念一定要弄懂。
3.3.1 Shallow heap
Shallow size就是對(duì)象本身占用內(nèi)存的大小,不包含其引用的對(duì)象。
- 常規(guī)對(duì)象(非數(shù)組)的Shallow size有其成員變量的數(shù)量和類型決定。
- 數(shù)組的shallow size有數(shù)組元素的類型(對(duì)象類型、基本類型)和數(shù)組長(zhǎng)度決定
因?yàn)椴幌馽++的對(duì)象本身可以存放大量?jī)?nèi)存,java的對(duì)象成員都是些引用。真正的內(nèi)存都在堆上,看起來(lái)是一堆原生的byte[], char[], int[],所以我們?nèi)绻豢磳?duì)象本身的內(nèi)存,那么數(shù)量都很小。所以我們看到Histogram圖是以Shallow size進(jìn)行排序的,排在第一位第二位的是byte,char 。
3.3.2 Retained Heap
Retained Heap的概念,它表示如果一個(gè)對(duì)象被釋放掉,那會(huì)因?yàn)樵搶?duì)象的釋放而減少引用進(jìn)而被釋放的所有的對(duì)象(包括被遞歸釋放的)所占用的heap大小。于是,如果一個(gè)對(duì)象的某個(gè)成員new了一大塊int數(shù)組,那這個(gè)int數(shù)組也可以計(jì)算到這個(gè)對(duì)象中。相對(duì)于shallow heap,Retained heap可以更精確的反映一個(gè)對(duì)象實(shí)際占用的大小(因?yàn)槿绻搶?duì)象釋放,retained heap都可以被釋放)。
這里要說(shuō)一下的是,Retained Heap并不總是那么有效。例如我在A里new了一塊內(nèi)存,賦值給A的一個(gè)成員變量。此時(shí)我讓B也指向這塊內(nèi)存。此時(shí),因?yàn)锳和B都引用到這塊內(nèi)存,所以A釋放時(shí),該內(nèi)存不會(huì)被釋放。所以這塊內(nèi)存不會(huì)被計(jì)算到A或者B的Retained Heap中。為了糾正這點(diǎn),MAT中的Leading Object(例如A或者B)不一定只是一個(gè)對(duì)象,也可以是多個(gè)對(duì)象。此時(shí),(A, B)這個(gè)組合的Retained Set就包含那塊大內(nèi)存了。對(duì)應(yīng)到MAT的UI中,在Histogram中,可以選擇Group By class, superclass or package來(lái)選擇這個(gè)組。
為了計(jì)算Retained Memory,MAT引入了Dominator Tree。加入對(duì)象A引用B和C,B和C又都引用到D(一個(gè)菱形)。此時(shí)要計(jì)算Retained Memory,A的包括A本身和B,C,D。B和C因?yàn)楣餐肈,所以他倆的Retained Memory都只是他們本身。D當(dāng)然也只是自己。我覺(jué)得是為了加快計(jì)算的速度,MAT改變了對(duì)象引用圖,而轉(zhuǎn)換成一個(gè)對(duì)象引用樹(shù)。在這里例子中,樹(shù)根是A,而B(niǎo),C,D是他的三個(gè)兒子。B,C,D不再有相互關(guān)系。把引用圖變成引用樹(shù),計(jì)算Retained Heap就會(huì)非常方便,顯示也非常方便。對(duì)應(yīng)到MAT UI上,在dominator tree這個(gè)view中,顯示了每個(gè)對(duì)象的shallow heap和retained heap。然后可以以該節(jié)點(diǎn)位樹(shù)根,一步步的細(xì)化看看retained heap到底是用在什么地方了。要說(shuō)一下的是,這種從圖到樹(shù)的轉(zhuǎn)換確實(shí)方便了內(nèi)存分析,但有時(shí)候會(huì)讓人有些疑惑。本來(lái)對(duì)象B是對(duì)象A的一個(gè)成員,但因?yàn)锽還被C引用,所以B在樹(shù)中并不在A下面,而很可能是平級(jí)。
為了糾正這點(diǎn),MAT中點(diǎn)擊右鍵,可以List objects中選擇with outgoing references和with incoming references。這是個(gè)真正的引用圖的概念,
- outgoing references :表示該對(duì)象的出節(jié)點(diǎn)(被該對(duì)象引用的對(duì)象)。
- incoming references :表示該對(duì)象的入節(jié)點(diǎn)(引用到該對(duì)象的對(duì)象)。
為了更好地理解Retained Heap,下面引用一個(gè)例子來(lái)說(shuō)明:
把內(nèi)存中的對(duì)象看成下圖中的節(jié)點(diǎn),并且對(duì)象和對(duì)象之間互相引用。這里有一個(gè)特殊的節(jié)點(diǎn)GC Roots,這就是reference chain(引用鏈)的起點(diǎn):?
?
從obj1入手,上圖中藍(lán)色節(jié)點(diǎn)代表僅僅只有通過(guò)obj1才能直接或間接訪問(wèn)的對(duì)象。因?yàn)榭梢酝ㄟ^(guò)GC Roots訪問(wèn),所以左圖的obj3不是藍(lán)色節(jié)點(diǎn);而在右圖卻是藍(lán)色,因?yàn)樗呀?jīng)被包含在retained集合內(nèi)。?
所以對(duì)于左圖,obj1的retained size是obj1、obj2、obj4的shallow size總和;?
右圖的retained size是obj1、obj2、obj3、obj4的shallow size總和。?
obj2的retained size可以通過(guò)相同的方式計(jì)算。
GC Root
GC發(fā)現(xiàn)通過(guò)任何reference chain(引用鏈)無(wú)法訪問(wèn)某個(gè)對(duì)象的時(shí)候,該對(duì)象即被回收。名詞GC Roots正是分析這一過(guò)程的起點(diǎn),例如JVM自己確保了對(duì)象的可到達(dá)性(那么JVM就是GC Roots),所以GC Roots就是這樣在內(nèi)存中保持對(duì)象可到達(dá)性的,一旦不可到達(dá),即被回收。通常GC Roots是一個(gè)在current thread(當(dāng)前線程)的call stack(調(diào)用棧)上的對(duì)象(例如方法參數(shù)和局部變量),或者是線程自身或者是system class loader(系統(tǒng)類加載器)加載的類以及native code(本地代碼)保留的活動(dòng)對(duì)象。所以GC Roots是分析對(duì)象為何還存活于內(nèi)存中的利器。
MAT中的一些有用的視圖
Thread OvewView
Thread OvewView可以查看這個(gè)應(yīng)用的Thread信息:?
Group
在Histogram和Domiantor Tree界面,可以選擇將結(jié)果用另一種Group的方式顯示(默認(rèn)是Group by Object),切換到Group by package,可以更好地查看具體是哪個(gè)包里的類占用內(nèi)存大,也很容易定位到自己的應(yīng)用程序。?
Path to GC Root
在Histogram或者Domiantor Tree的某一個(gè)條目上,右鍵可以查看其GC Root Path:?
這里也要說(shuō)明一下Java的引用規(guī)則:?
從最強(qiáng)到最弱,不同的引用(可到達(dá)性)級(jí)別反映了對(duì)象的生命周期。
- Strong Ref(強(qiáng)引用):通常我們編寫的代碼都是Strong Ref,于此對(duì)應(yīng)的是強(qiáng)可達(dá)性,只有去掉強(qiáng)可達(dá),對(duì)象才被回收。
- Soft Ref(軟引用):對(duì)應(yīng)軟可達(dá)性,只要有足夠的內(nèi)存,就一直保持對(duì)象,直到發(fā)現(xiàn)內(nèi)存吃緊且沒(méi)有Strong Ref時(shí)才回收對(duì)象。一般可用來(lái)實(shí)現(xiàn)緩存,通過(guò)java.lang.ref.SoftReference類實(shí)現(xiàn)。
- Weak Ref(弱引用):比Soft Ref更弱,當(dāng)發(fā)現(xiàn)不存在Strong Ref時(shí),立刻回收對(duì)象而不必等到內(nèi)存吃緊的時(shí)候。通過(guò)java.lang.ref.WeakReference和java.util.WeakHashMap類實(shí)現(xiàn)。
- Phantom Ref(虛引用):根本不會(huì)在內(nèi)存中保持任何對(duì)象,你只能使用Phantom Ref本身。一般用于在進(jìn)入finalize()方法后進(jìn)行特殊的清理過(guò)程,通過(guò) java.lang.ref.PhantomReference實(shí)現(xiàn)。
點(diǎn)擊Path To GC Roots —> with all references?
參考文檔
Java的內(nèi)存泄露的特點(diǎn)
- Java中的內(nèi)存泄露主要特征:可達(dá),無(wú)用
- 無(wú)用指的是創(chuàng)建了但是不再使用之后沒(méi)有釋放
- 能重用但是卻創(chuàng)建了新的對(duì)象進(jìn)行處理
MAT使用技巧進(jìn)階
使用Android Studio Dump內(nèi)存文件
Android Studio的最新版本可以直接獲取hprof文件:
然后選擇文件,點(diǎn)擊右鍵轉(zhuǎn)換成標(biāo)準(zhǔn)的hprof文件,就可以在MAT中打開(kāi)了。
在使用使用Eclipse或者AndroidStudio抓內(nèi)存之前,一定要手動(dòng)點(diǎn)擊 Initiate GC按鈕手動(dòng)觸發(fā)GC,這樣抓到的內(nèi)存使用情況就是不包括Unreachable對(duì)象的。
Unreachable對(duì)象
Unreachable指的是可以被垃圾回收器回收的對(duì)象,但是由于沒(méi)有GC發(fā)生,所以沒(méi)有釋放,這時(shí)抓的內(nèi)存使用中的Unreachable就是這些對(duì)象。
點(diǎn)擊Calculate Retained Size之后,會(huì)出現(xiàn)Retained Size這一列
可以看到Unreachable Object的對(duì)象其Retained Heap值都為0.這也是正常的。
Histogram
MAT中Histogram的主要作用是查看一個(gè)instance的數(shù)量,一般用來(lái)查看自己創(chuàng)建的類的實(shí)例的個(gè)數(shù)。
- 可以很容易的找出占用內(nèi)存最多的幾個(gè)對(duì)象,根據(jù)Percentage(百分比)來(lái)排序。
- 可以分不同維度來(lái)查看對(duì)象的Dominator Tree視圖,Group by class、Group by class loader、Group by package
和Histogram類似,時(shí)間久了,通過(guò)多次對(duì)比也可以把溢出對(duì)象找出來(lái)。 - Dominator Tree和Histogram的區(qū)別是站的角度不一樣,Histogram是站在類的角度上去看,Dominator Tree是站的對(duì)象實(shí)例的角度上看,Dominator Tree可以更方便的看出其引用關(guān)系。
通過(guò)查看Object的個(gè)數(shù),結(jié)合代碼就可以找出存在內(nèi)存泄露的類(即可達(dá)但是無(wú)用的對(duì)象,或者是可以重用但是重新創(chuàng)建的對(duì)象)
Histogram中還可以對(duì)對(duì)象進(jìn)行Group,更方便查看自己Package中的對(duì)象信息。
Thread信息
MAT中可以查看當(dāng)前的Thread信息:
從圖中可以得到的信息:
可以看到可能有內(nèi)存問(wèn)題的Thread:
可以看到數(shù)量可能有問(wèn)題的Thread
幫助信息
MAT中的各個(gè)視圖中,在每一個(gè)Item中點(diǎn)擊右鍵會(huì)出現(xiàn)很多選項(xiàng),很多時(shí)候我們需要依賴這些選項(xiàng)來(lái)進(jìn)行分析:
這些選項(xiàng)的具體含義則可以通過(guò)右鍵中的Search Queries這個(gè)選項(xiàng)(上圖中的倒數(shù)第四個(gè)選項(xiàng))進(jìn)行搜索和查看,非常的有用。
可以看到,所有的命令其實(shí)就是配置不同的SQL查詢語(yǔ)句。
比如我們最常用的:
- List objects -> with incoming references:查看這個(gè)對(duì)象持有的外部對(duì)象引用
- List objects -> with outcoming references:查看這個(gè)對(duì)象被哪些外部對(duì)象引用
- Path To GC Roots -> exclude all phantim/weak/soft etc. references:查看這個(gè)對(duì)象的GC Root,不包含虛、弱引用、軟引用,剩下的就是強(qiáng)引用。從GC上說(shuō),除了強(qiáng)引用外,其他的引用在JVM需要的情況下是都可以 被GC掉的,如果一個(gè)對(duì)象始終無(wú)法被GC,就是因?yàn)閺?qiáng)引用的存在,從而導(dǎo)致在GC的過(guò)程中一直得不到回收,因此就內(nèi)存溢出了。
- Path To GC Roots -> exclude weak/soft references:查看這個(gè)對(duì)象的GC Root,不含弱引用和軟引用所有的引用.
- Merge Shortest path to GC root?:找到從GC根節(jié)點(diǎn)到一個(gè)對(duì)象或一組對(duì)象的共同路徑
Debug Bitmap
如果經(jīng)常使用MAT分析內(nèi)存,就會(huì)發(fā)現(xiàn)Bitmap所占用的內(nèi)存是非常大的,這個(gè)和其實(shí)際顯示面積是有關(guān)系的。在2K屏幕上,一張Bitmap能達(dá)到20MB的大小。
所以要是MAT提供了一種方法,可以將存儲(chǔ)Bitmap的byte數(shù)組導(dǎo)出來(lái),使用第三方工具打開(kāi)。這個(gè)大大提高了我們分析內(nèi)存泄露的效率。
關(guān)于這個(gè)方法的操作流程,可以參考這篇文章還原MAT中的Bitmap圖像.
Debug ArrayList
ArrayList是使用非常常用的一個(gè)數(shù)據(jù)結(jié)構(gòu),在MAT中,如果想知道ArrayList中有哪些數(shù)據(jù),需要右鍵-> List Objects -> With outgoing references,然后可以看到下面的圖:
從上圖可以看到,這個(gè)ArrayList的內(nèi)容在一個(gè)array數(shù)組中,即暴漏了ArrayList的內(nèi)部結(jié)構(gòu),查看的時(shí)候有點(diǎn)不方便,所以MAT提供了另外一種查看ArrayList內(nèi)數(shù)據(jù)的方式:
其結(jié)果非常直觀:
Big Drops In Dominator Tree
Big Drops In Dominator Tree選項(xiàng)在右鍵->
Displays memory accumulation points in the dominator tree. Displayed are objects with a big difference between the retained size of the parent and the children and the first “interesting” dominator of the accumulation point. These are places where the memory of many small objects is accumulated under one object.
?
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/6044439.html
總結(jié)
- 上一篇: Java 8 Stream API详解-
- 下一篇: 数据扩展性探讨和总结--转