Android SDK提供了2個主要的剖析應用程序內存使用情況的工具

Dalvik 虛擬機支持垃圾收集,但是這不意味着你可以不用關心內存管理。你應該格外注意移動設備的內存使用,在上面內存空間是受到限制的。在這篇 文章裏面,我們來看看Android SDK裏面的一些內存剖析工具(profiling tools)是如何幫助我們修整應用程序的內存使用。


一些內存使用問題是很明顯的,例如,如果在每次用戶觸摸屏幕的時候應用程序有內存泄露,將會有可能觸發OutOfMemoryError,最終程序崩潰。另外一些問題卻很微妙,也許只是降低應用程序和整個系統的性能(當高頻率和長時間地運行垃圾收集器的時候)。

必要的工具: 

Android SDK提供了2個主要的剖析應用程序內存使用情況的工具:DDMS裏的一個分頁Allocation Tracker和heap dumps。Allocation Tracker是很有用的,特別是當你想得到程序在一定的時間裏內存的分配情況的一種感性認識的時候。但是它不能給你任何關於程序heap總體情況的任何 信息。關於Allocation Tracker的更多信息,請看文章Tracking Memory Allocations 。文章剩下的內容將把重點放在heap dumps,它是更強大的內存分析工具。


一個heap dump就是一個程序heap的快照,它保存爲一種叫做HPROF的二進制格式。Dalvik用的也是類似的格式,但是不完全一樣。這裏是Java的HPROF工具 。有很多方法去生成一個運行時應用程序的heap dump。其中一種就是使用在DDMS裏邊的Dump HPROF file按鈕。如果想產生更精確的dump數據,可以在程序中使用android.os.Debug.dumpHprofData() 方法。

分析heap dump,你可以使用一些標準的工具比如jhat 或者Eclipse Memory Analyzer(MAT) 。不過,首先你需要把.hprof文件從Dalvik格式轉換成J2SE HPROF格式。你可以使用Android SDK提供的hprof-conv工具。例如:

  1. hprof-conv dump.hprof converted-dump.hprof  

調試一個內存泄露實例:


在Dalvik運行時裏邊,程序員不能顯式地分配和釋放內存,所以這裏的內存泄露跟c和c++裏面的不同。在你的代碼裏邊,內存泄露就是你保留了一個並不再需要的類對象的引用。有時候僅僅一個引用就會阻礙gc對一大堆對象的回收。

我們來過一個實際的例子,Android SDK裏面提供的範例程序Honeycomb Gallery sample app 。它是一個photo gallery程序,用來演示一些新的Honeycomb API的使用。(下載和編譯這些代碼,請看這些命令 。)我們會有意地加入一個內存泄露在程序裏邊,然後來演示如何調試它。

 

想象一下我們想修改程序讓它從網絡下載圖片。爲了讓它更具備靈活性,我們可以考慮實現一個緩存,保存最近查看過的圖片。我們可以對ContentFragment.java做一些小的修改來達到這個目的。在class頂部,我們增加一個新的靜態變量:

  1. private static HashMap<String,Bitmap> sBitmapCache = new HashMap<String,Bitmap>();  

這裏就是我們保存緩存的地方。現在我們可以修改updateContentAndRecycleBitmap()方法,讓它在下載之前先查看是否數據已經存在,如果不存在就去下載,然後添加數據到緩存。

  1. void updateContentAndRecycleBitmap(int category, int position) {  
  2.     if (mCurrentActionMode != null) {  
  3.         mCurrentActionMode.finish();  
  4.     }  
  5.    
  6.     // Get the bitmap that needs to be drawn and update the ImageView.  
  7.    
  8.     // Check if the Bitmap is already in the cache  
  9.     String bitmapId = "" + category + "." + position;  
  10.     mBitmap = sBitmapCache.get(bitmapId);  
  11.    
  12.     if (mBitmap == null) {  
  13.         // It's not in the cache, so load the Bitmap and add it to the cache.  
  14.         // DANGER! We add items to this cache without ever removing any.  
  15.         mBitmap = Directory.getCategory(category).getEntry(position)  
  16.                 .getBitmap(getResources());  
  17.         sBitmapCache.put(bitmapId, mBitmap);  
  18.     }  
  19.     ((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap);  
  20. }  

我已經在這裏故意引入了一個內存泄露的問題:我們把圖片加入了緩存但是從來沒有移除他們。在真實的應用裏,我們可以會用某種方法來限制緩存的大小。

 

在DDMS裏檢查heap的使用情況

 

Dalvik Debug Monitor Server(DDMS)是主要的Android調試工具之一,也是ADT Eclipse plug-in 的一部分,獨立的程序版本也可以在Android SDK的根目錄下的tools/下面找到。關於DDMS更多的信息,請參考使用DDMS 。

 

我們來使用DDMS檢查這個應用的heap使用情況。你可以使用下面的兩種方法啓動DDMS:

  • from Eclipse: click Window > Open Perspective > Other... > DDMS
  • or from the command line: run ddms (or ./ddms on Mac/Linux) in the tools/ directory

\

 

在 左邊的面板選擇進程com.example.android.hcgallery,然後在 工具條上邊點擊Show heap updates按鈕。這個時候切換到DDMS的VM Heap分頁。它會顯示每次gc後heap內存的一些基本數據。要看第一次gc後的數據內容,點擊Cause GC按鈕:

 

\

 

我 們可以看到現在的值(Allocated列)是有一些超過8MB。現在滑動相片,這時看到 數據在增大。因爲只有僅僅13個相片在程序裏邊,所以泄露的內存只有這麼大。在某種程度上來說,這時最壞的一種內存泄露,因爲我們沒法得到 OutOfMemoryError來提醒我們說現在內存溢出了。

 

生成heap dump

 

我們現在使用heap dump來追蹤這個問題。點擊DDMS工具條上面的Dump HPROF文件按鈕,選擇文件存儲位置,然後在運行hprof-conv。在這個例子裏我們使用獨立的MAT版本(版本1.0.1),從MAT站點下載 。

 

如果你使用ADT(它包含DDMS的插件)同時也在eclipse裏面安裝了MAT,點擊“dump HPROF”按鈕將會自動地做轉換(用hprof-conv)同時會在eclipse裏面打開轉換後的hprof文件(它其實用MAT打開)。

 

用MAT分析heap dumps

啓 動MAT然後加載剛纔我們生成的HPROF文件。MAT是一個強大的工具,講述它所有的特性超出了本文的範圍,所以我只想演示一種你可以用來檢測 泄露的方法:直方圖(Histogram)視圖。它顯示了一個可以排序的類實例的列表,內容包括:shallow heap(所有實例的內存使用總和),或者retained heap(所有類實例被分配的內存總和,裏面也包括他們所有引用的對象)。

\

 

如 果我們按照shallow heap排序,我們可以看到byte[]實例在頂端。自從Android3.0(Honeycomb),Bitmap的像素數據被存儲在byte數組裏 (之前是被存儲在Dalvik的heap裏),所以基於這個對象的大小來判斷,不用說它一定是我們泄露掉的bitmap。

 

右擊byte[]類然後選擇List Objects > with incoming references。它會生成一個heap上的所有byte數組的列表,在列表裏,我們可以按照Shallow Heap的使用情況來排序。

 

選擇並展開一個比較大的對象,它將展示從根到這個對象的路徑--就是一條保證對象有效的鏈條。注意看,這個就是我們的bitmap緩存!

\

 

MAT不會明確告訴我們這就是泄露,因爲它也不知道這個東西是不是程序還需要的,只有程序員知道。在這個案例裏面,緩存使用的大量的內存會影響到後面的應用程序,所以我們可以考慮限制緩存的大小。

 

使用MAT比較heap dumps

 

調試內存泄露時,有時候適時比較2個地方的heap狀態是很有用的。這時你就需要生成2個單獨的HPROF文件(不要忘了轉換格式)。下面是一些關於如何在MAT裏比較2個heap dumps的內容(有一點複雜):

  1. 第一個HPROF 文件(using File > Open Heap Dump ).
  2. 打開 Histogram view.
  3. 在Navigation History view裏 (如果看不到就從Window > Navigation History找 ), 右擊histogram然後選擇Add to Compare Basket .
  4. 打開第二個HPROF 文件然後重做步驟2和3.
  5. 切換到Compare Basket view, 然後點擊Compare the Results (視圖右上角的紅色"!"圖標)。

總結

這 本篇文章裏面,我展示了Allocation Tracker和heap dumps是如何給你一種對程序內存使用的感性認識。我也展示了Eclipse Memory Analyzer(MAT)可以幫助追逐我們程序裏面的內存泄露問題。MAT是一個強大的工具,我也僅僅觸碰了一些皮毛,如果你想學習更多內容,我建議讀 一些下面的文章:

  • Memory Analyzer News : Eclipse MAT project的官方博客。
  • Markus Kohler的Java Performance blog有很多有用的文章, 包括 Analysing the Memory Usage of Android Applications with the Eclipse Memory Analyzer and 10 Useful Tips for the Eclipse Memory Analyzer
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章