Android 內存分析工具

openkk 12年前發布 | 77K 次閱讀 Android Android開發 移動開發

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  
  2. </ol> </div>

    調試一個內存泄露實例:


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

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

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

        private static HashMap sBitmapCache = new HashMap();  

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

          void updateContentAndRecycleBitmap(int category, int position) {
      if (mCurrentActionMode != null) {
      mCurrentActionMode.finish();
      }

          // Get the bitmap that needs to be drawn and update the ImageView.  
      
          // Check if the Bitmap is already in the cache  
          String bitmapId = "" + category + "." + position;  
          mBitmap = sBitmapCache.get(bitmapId);  
      
          if (mBitmap == null) {  
              // It's not in the cache, so load the Bitmap and add it to the cache.  
              // DANGER! We add items to this cache without ever removing any.  
              mBitmap = Directory.getCategory(category).getEntry(position)  
                      .getBitmap(getResources());  
              sBitmapCache.put(bitmapId, mBitmap);  
          }  
          ((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap);  
      }  </pre></ol>
      

      </div>

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

       

      在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
      • </ul>

        Android 內存分析工具

         

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

         

        Android 內存分析工具

         

        我 們可以看到現在的值(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(所有類實例被分配的內存總和,里面也包括他們所有引用的對象)。

        Android 內存分析工具

         

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

         

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

         

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

        Android 內存分析工具

         

        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 (視圖右上角的紅色"!"圖標)。
        6. </ol>

          總結

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

     本文由用戶 openkk 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
     轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
     本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!