Dalvik虛擬機(jī)支持垃圾收集,但是這不意味著你可以不用關(guān)心內(nèi)存管理。你應(yīng)該格外注意移動(dòng)設(shè)備的內(nèi)存使用,在上面內(nèi)存空間是受到限制的。在這篇文章里面,我們來(lái)看看Android SDK里面的一些內(nèi)存剖析工具(profiling tools)是如何幫助我們修整應(yīng)用程序的內(nèi)存使用。
一些內(nèi)存使用問(wèn)題是很明顯的,例如,如果在每次用戶觸摸屏幕的時(shí)候應(yīng)用程序有內(nèi)存泄露,將會(huì)有可能觸發(fā)OutOfMemoryError,最終程序崩潰。另外一些問(wèn)題卻很微妙,也許只是降低應(yīng)用程序和整個(gè)系統(tǒng)的性能(當(dāng)高頻率和長(zhǎng)時(shí)間地運(yùn)行垃圾收集器的時(shí)候)。
必要的工具:
Android SDK提供了2個(gè)主要的剖析應(yīng)用程序內(nèi)存使用情況的工具:DDMS里的一個(gè)分頁(yè)Allocation Tracker和heap dumps。Allocation Tracker是很有用的,特別是當(dāng)你想得到程序在一定的時(shí)間里內(nèi)存的分配情況的一種感性認(rèn)識(shí)的時(shí)候。但是它不能給你任何關(guān)于程序heap總體情況的任何信息。關(guān)于Allocation Tracker的更多信息,請(qǐng)看文章Tracking Memory Allocations 。文章剩下的內(nèi)容將把重點(diǎn)放在heap dumps,它是更強(qiáng)大的內(nèi)存分析工具。
一個(gè)heap dump就是一個(gè)程序heap的快照,它保存為一種叫做HPROF的二進(jìn)制格式。Dalvik用的也是類(lèi)似的格式,但是不完全一樣。這里是Java的HPROF工具 。有很多方法去生成一個(gè)運(yùn)行時(shí)應(yīng)用程序的heap dump。其中一種就是使用在DDMS里邊的Dump HPROF file按鈕。如果想產(chǎn)生更精確的dump數(shù)據(jù),可以在程序中使用android.os.Debug.dumpHprofData() 方法。
分析heap dump,你可以使用一些標(biāo)準(zhǔn)的工具比如jhat 或者Eclipse Memory Analyzer(MAT) 。不過(guò),首先你需要把.hprof文件從Dalvik格式轉(zhuǎn)換成J2SE HPROF格式。你可以使用Android SDK提供的hprof-conv工具。例如:
調(diào)試一個(gè)內(nèi)存泄露實(shí)例:
在Dalvik運(yùn)行時(shí)里邊,程序員不能顯式地分配和釋放內(nèi)存,所以這里的內(nèi)存泄露跟c和c++里面的不同。在你的代碼里邊,內(nèi)存泄露就是你保留了一個(gè)并不再需要的類(lèi)對(duì)象的引用。有時(shí)候僅僅一個(gè)引用就會(huì)阻礙gc對(duì)一大堆對(duì)象的回收。
我們來(lái)過(guò)一個(gè)實(shí)際的例子,Android SDK里面提供的范例程序Honeycomb Gallery sample app 。它是一個(gè)photo gallery程序,用來(lái)演示一些新的Honeycomb API的使用。(下載和編譯這些代碼,請(qǐng)看這些命令 。)我們會(huì)有意地加入一個(gè)內(nèi)存泄露在程序里邊,然后來(lái)演示如何調(diào)試它。
想象一下我們想修改程序讓它從網(wǎng)絡(luò)下載圖片。為了讓它更具備靈活性,我們可以考慮實(shí)現(xiàn)一個(gè)緩存,保存最近查看過(guò)的圖片。我們可以對(duì)ContentFragment.java做一些小的修改來(lái)達(dá)到這個(gè)目的。在class頂部,我們?cè)黾右粋€(gè)新的靜態(tài)變量:
這里就是我們保存緩存的地方?,F(xiàn)在我們可以修改updateContentAndRecycleBitmap()方法,讓它在下載之前先查看是否數(shù)據(jù)已經(jīng)存在,如果不存在就去下載,然后添加數(shù)據(jù)到緩存。
我已經(jīng)在這里故意引入了一個(gè)內(nèi)存泄露的問(wèn)題:我們把圖片加入了緩存但是從來(lái)沒(méi)有移除他們。在真實(shí)的應(yīng)用里,我們可以會(huì)用某種方法來(lái)限制緩存的大小。
在DDMS里檢查heap的使用情況
Dalvik Debug Monitor Server(DDMS)是主要的Android調(diào)試工具之一,也是ADT Eclipse plug-in 的一部分,獨(dú)立的程序版本也可以在Android SDK的根目錄下的tools/下面找到。關(guān)于DDMS更多的信息,請(qǐng)參考使用DDMS 。
我們來(lái)使用DDMS檢查這個(gè)應(yīng)用的heap使用情況。你可以使用下面的兩種方法啟動(dòng)DDMS:
ddms
(or ./ddms
on Mac/Linux) in thetools/
directory 在左邊的面板選擇進(jìn)程com.example.android.hcgallery,然后在工具條上邊點(diǎn)擊Show heap updates按鈕。這個(gè)時(shí)候切換到DDMS的VM Heap分頁(yè)。它會(huì)顯示每次gc后heap內(nèi)存的一些基本數(shù)據(jù)。要看第一次gc后的數(shù)據(jù)內(nèi)容,點(diǎn)擊Cause GC按鈕:
我們可以看到現(xiàn)在的值(Allocated列)是有一些超過(guò)8MB?,F(xiàn)在滑動(dòng)相片,這時(shí)看到數(shù)據(jù)在增大。因?yàn)橹挥袃H僅13個(gè)相片在程序里邊,所以泄露的內(nèi)存只有這么大。在某種程度上來(lái)說(shuō),這時(shí)最壞的一種內(nèi)存泄露,因?yàn)槲覀儧](méi)法得到 OutOfMemoryError來(lái)提醒我們說(shuō)現(xiàn)在內(nèi)存溢出了。
生成heap dump
我們現(xiàn)在使用heap dump來(lái)追蹤這個(gè)問(wèn)題。點(diǎn)擊DDMS工具條上面的Dump HPROF文件按鈕,選擇文件存儲(chǔ)位置,然后在運(yùn)行hprof-conv。在這個(gè)例子里我們使用獨(dú)立的MAT版本(版本1.0.1),從MAT站點(diǎn)下載 。
如果你使用ADT(它包含DDMS的插件)同時(shí)也在eclipse里面安裝了MAT,點(diǎn)擊“dump HPROF”按鈕將會(huì)自動(dòng)地做轉(zhuǎn)換(用hprof-conv)同時(shí)會(huì)在eclipse里面打開(kāi)轉(zhuǎn)換后的hprof文件(它其實(shí)用MAT打開(kāi))。
啟動(dòng)MAT然后加載剛才我們生成的HPROF文件。MAT是一個(gè)強(qiáng)大的工具,講述它所有的特性超出了本文的范圍,所以我只想演示一種你可以用來(lái)檢測(cè)泄露的方法:直方圖(Histogram)視圖。它顯示了一個(gè)可以排序的類(lèi)實(shí)例的列表,內(nèi)容包括:shallow heap(所有實(shí)例的內(nèi)存使用總和),或者retained heap(所有類(lèi)實(shí)例被分配的內(nèi)存總和,里面也包括他們所有引用的對(duì)象)。
如果我們按照shallow heap排序,我們可以看到byte[]實(shí)例在頂端。自從Android3.0(Honeycomb),Bitmap的像素?cái)?shù)據(jù)被存儲(chǔ)在byte數(shù)組里(之前是被存儲(chǔ)在Dalvik的heap里),所以基于這個(gè)對(duì)象的大小來(lái)判斷,不用說(shuō)它一定是我們泄露掉的bitmap。
右擊byte[]類(lèi)然后選擇List Objects > with incoming references。它會(huì)生成一個(gè)heap上的所有byte數(shù)組的列表,在列表里,我們可以按照Shallow Heap的使用情況來(lái)排序。
選擇并展開(kāi)一個(gè)比較大的對(duì)象,它將展示從根到這個(gè)對(duì)象的路徑--就是一條保證對(duì)象有效的鏈條。注意看,這個(gè)就是我們的bitmap緩存!
MAT不會(huì)明確告訴我們這就是泄露,因?yàn)樗膊恢肋@個(gè)東西是不是程序還需要的,只有程序員知道。在這個(gè)案例里面,緩存使用的大量的內(nèi)存會(huì)影響到后面的應(yīng)用程序,所以我們可以考慮限制緩存的大小。
使用MAT比較heap dumps
調(diào)試內(nèi)存泄露時(shí),有時(shí)候適時(shí)比較2個(gè)地方的heap狀態(tài)是很有用的。這時(shí)你就需要生成2個(gè)單獨(dú)的HPROF文件(不要忘了轉(zhuǎn)換格式)。下面是一些關(guān)于如何在MAT里比較2個(gè)heap dumps的內(nèi)容(有一點(diǎn)復(fù)雜):
總結(jié)
這本篇文章里面,我展示了Allocation Tracker和heap dumps是如何給你一種對(duì)程序內(nèi)存使用的感性認(rèn)識(shí)。我也展示了Eclipse Memory Analyzer(MAT)可以幫助追逐我們程序里面的內(nèi)存泄露問(wèn)題。MAT是一個(gè)強(qiáng)大的工具,我也僅僅觸碰了一些皮毛,如果你想學(xué)習(xí)更多內(nèi)容,我建議讀一些下面的文章:
聯(lián)系客服