1. 程式人生 > >DDMS的使用、記憶體溢位的除錯

DDMS的使用、記憶體溢位的除錯

三、記憶體監測工具 DDMS --> Heap

    無論怎麼小心,想完全避免bad code是不可能的,此時就需要一些工具來幫助我們檢查程式碼中是否存在會造成記憶體洩漏的地方。Android tools中的DDMS就帶有一個很不錯的記憶體監測工具Heap(這裡我使用eclipse的ADT外掛,並以真機為例,在模擬器中的情況類似)。用Heap監測應用程序使用記憶體情況的步驟如下:

1. 啟動eclipse後,切換到DDMS透檢視,並確認Devices檢視、Heap檢視都是開啟的;

2. 將手機通過USB連結至電腦,連結時需要確認手機是處於“USB除錯”模式,而不是作為“Mass Storage”;

3. 連結成功後,在DDMS的Devices檢視中將會顯示手機裝置的序列號,以及裝置中正在執行的部分程序資訊;

4. 點選選中想要監測的程序,比如system_process程序;

5. 點選選中Devices檢視介面中最上方一排圖示中的“Update Heap”圖示;

6. 點選Heap檢視中的“Cause GC”按鈕;

7. 此時在Heap檢視中就會看到當前選中的程序的記憶體使用量的詳細情況[如圖所示]。

說明:

a) 點選“Cause GC”按鈕相當於向虛擬機器請求了一次gc操作;

b) 當記憶體使用資訊第一次顯示以後,無須再不斷的點選“Cause GC”,Heap檢視介面會定時重新整理,在對應用的不斷的操作過程中就可以看到記憶體使用的變化;

c) 記憶體使用資訊的各項引數根據名稱即可知道其意思,在此不再贅述。

    如何才能知道我們的程式是否有記憶體洩漏的可能性呢。這裡需要注意一個值:Heap檢視中部有一個Type叫做data object,即資料物件,也就是我們的程式中大量存在的類型別的物件。在data object一行中有一列是“Total Size”,其值就是當前程序中所有Java資料物件的記憶體總量,一般情況下,這個值的大小決定了是否會有記憶體洩漏。可以這樣判斷:

a) 不斷的操作當前應用,同時注意觀察data object的Total Size值;

b) 正常情況下Total Size值都會穩定在一個有限的範圍內,也就是說由於程式中的的程式碼良好,沒有造成物件不被垃圾回收的情況,所以說雖然我們不斷的操作會不斷的生成很多物件,而在虛擬機器不斷的進行GC的過程中,這些物件都被回收了,記憶體佔用量會會落到一個穩定的水平;

c) 反之如果程式碼中存在沒有釋放物件引用的情況,則data object的Total Size值在每次GC後不會有明顯的回落,隨著操作次數的增多Total Size的值會越來越大,

    直到到達一個上限後導致程序被kill掉。

d) 此處已system_process程序為例,在我的測試環境中system_process程序所佔用的記憶體的data object的Total Size正常情況下會穩定在2.2~2.8之間,而當其值超過3.55後進程就會被kill。

    總之,使用DDMS的Heap檢視工具可以很方便的確認我們的程式是否存在記憶體洩漏的可能性。

四、記憶體分析工具 MAT(Memory Analyzer Tool)

    如果使用DDMS確實發現了我們的程式中存在記憶體洩漏,那又如何定位到具體出現問題的程式碼片段,最終找到問題所在呢?如果從頭到尾的分析程式碼邏輯,那肯定會把人逼瘋,特別是在維護別人寫的程式碼的時候。這裡介紹一個極好的記憶體分析工具 -- Memory Analyzer Tool(MAT)。

    MAT是一個Eclipse外掛,同時也有單獨的RCP客戶端。官方下載地址、MAT介紹和詳細的使用教程請參見:www.eclipse.org/mat,在此不進行說明了。另外在MAT安裝後的幫助文件裡也有完備的使用教程。在此僅舉例說明其使用方法。我自己使用的是MAT的eclipse外掛,使用外掛要比RCP稍微方便一些。

    使用MAT進行記憶體分析需要幾個步驟,包括:生成.hprof檔案、開啟MAT並匯入.hprof檔案、使用MAT的檢視工具分析記憶體。以下詳細介紹。

(一) 生成.hprof檔案

    生成.hprof檔案的方法有很多,而且Android的不同版本中生成.hprof的方式也稍有差別,我使用的版本的是2.1,各個版本中生成.prof檔案的方法請參考:

http://android.git.kernel.org/?p=platform/dalvik.git;a=blob_plain;f=docs/heap-profiling.html;hb=HEAD。

1. 開啟eclipse並切換到DDMS透檢視,同時確認Devices、Heap和logcat檢視已經打開了;

2. 將手機裝置連結到電腦,並確保使用“USB 除錯”模式連結,而不是“Mass Storage“模式;

3. 連結成功後在Devices檢視中就會看到裝置的序列號,和裝置中正在執行的部分程序;

4. 點選選中想要分析的應用的程序,在Devices檢視上方的一行圖示按鈕中,同時選中“Update Heap”和“Dump HPROF file”兩個按鈕;

5. 這是DDMS工具將會自動生成當前選中程序的.hprof檔案,並將其進行轉換後存放在sdcard當中,如果你已經安裝了MAT外掛,那麼此時MAT將會自動被啟用,並開始對.hprof檔案進行分析;

    注意:第4步和第5步能夠正常使用前提是我們需要有sdcard,並且當前程序有向sdcard中寫入的許可權(WRITE_EXTERNAL_STORAGE),否則.hprof檔案不會被生成,在logcat中會顯示諸如

     ERROR/dalvikvm(8574): hprof: can't open /sdcard/com.xxx.hprof-hptemp: Permission denied. 

    的資訊。

    如果我們沒有sdcard,或者當前程序沒有向sdcard寫入的許可權(如system_process),那我們可以這樣做:

6. 在當前程式中,例如framework中某些程式碼中,可以使用android.os.Debug中的:

   public static void dumpHprofData(String fileName) throws IOException

   方法,手動的指定.hprof檔案的生成位置。例如:

   xxxButton.setOnClickListener(new View.OnClickListener() {

       public void onClick(View view) {

          android.os.Debug.dumpHprofData("/data/temp/myapp.hprof");

          ... ...

       }

   }

    上述程式碼意圖是希望在xxxButton被點選的時候開始抓取記憶體使用資訊,並儲存在我們指定的位置:/data/temp/myapp.hprof,這樣就沒有許可權的限制了,而且也無須用sdcard。但要保證/data/temp目錄是存在的。這個路徑可以自己定義,當然也可以寫成sdcard當中的某個路徑。

(二) 使用MAT匯入.hprof檔案

1. 如果是eclipse自動生成的.hprof檔案,可以使用MAT外掛直接開啟(可能是比較新的ADT才支援);

2. 如果eclipse自動生成的.hprof檔案不能被MAT直接開啟,或者是使用android.os.Debug.dumpHprofData()方法手動生成的.hprof檔案,則需要將.hprof檔案進行轉換,轉換的方法:

    例如我將.hprof檔案拷貝到PC上的/ANDROID_SDK/tools目錄下,並輸入命令hprof-conv xxx.hprof yyy.hprof,其中xxx.hprof為原始檔案,yyy.hprof為轉換過後的檔案。轉換過後的檔案自動放在/ANDROID_SDK/tools目錄下。OK,到此為止,.hprof檔案處理完畢,可以用來分析記憶體洩露情況了。

3. 在Eclipse中點選Windows->Open Perspective->Other->Memory Analyzer,或者打Memory Analyzer Tool的RCP。在MAT中點選File->Open File,瀏覽並匯入剛剛轉換而得到的.hprof檔案。

(三) 使用MAT的檢視工具分析記憶體

    匯入.hprof檔案以後,MAT會自動解析並生成報告,點選Dominator Tree,並按Package分組,選擇自己所定義的Package類點右鍵,在彈出選單中選擇List objects->With incoming references。這時會列出所有可疑類,右鍵點選某一項,並選擇Path to GC Roots -> exclude weak/soft references,會進一步篩選出跟程式相關的所有有記憶體洩露的類。據此,可以追蹤到程式碼中的某一個產生洩露的類。

    MAT的介面如下圖所示。

    具體的分析方法在此不做說明了,因為在MAT的官方網站和客戶端的幫助文件中有十分詳盡的介紹。

    瞭解MAT中各個檢視的作用很重要,例如www.eclipse.org/mat/about/screenshots.php中介紹的。

    總之使用MAT分析記憶體查詢記憶體洩漏的根本思路,就是找到哪個類的物件的引用沒有被釋放,找到沒有被釋放的原因,也就可以很容易定位程式碼中的哪些片段的邏輯有問題了

Android 記憶體使用hprof檔案開啟方法
與C++的記憶體不同,C++的記憶體洩露是由於分配了記憶體給某程式但是又沒有回收造成的。Java的記憶體洩露則是引用了一些垃圾物件,意思就是說程式引用了某些物件,但是又從來沒有使用過。
Jave中的引用分為3種:
強引用:引用為空的時候,Java的垃圾回收器會處理。一般來說自己寫的程式大部分都是強引用。
軟引用:堆記憶體不夠的時候,Java的垃圾回收器會處理這類引用。
弱引用:Jave的垃圾回收器每次都會回收這類引用。
如何用MAT來分析,前提是Android開發和測試的工具安裝完整,SDK,Eclipse:
1.開啟Eclipse
2.選擇 Help->Install New Software;
3.在Work with中新增站點:http://download.eclipse.org/mat/1.0/update-site/(這個地址可能會變化,但是新的地址可以在官方網站上找到:http://www.eclipse.org/mat/downloads.php
4.生成.hprof檔案:插入SD卡(Android機器很多程式都需要插入SD卡),並將裝置連線到PC,在Eclipse中的DDMS中選擇要測試的程序,然後點選Update Heap 和Dump HPROF file兩個Button。
.hprof 檔案會自動儲存在SD卡上,把 .hprof 檔案拷貝到PC上的\ android-sdk-windows\tools目錄下。這個由DDMS生成的檔案不能直接在MAT開啟,需要轉換。
執行cmd開啟命令列,cd到\ android-sdk-windows\tools所在目錄,並輸入命令hprof-conv xxxxx.hprof yyyyy.hprof,其中xxxxx.hprof為原始檔案,yyyyy.hprof為轉換過後的檔案。轉換過後的檔案自動放在android-sdk-windows\tools 目錄下。
OK,到此為止,.hprof檔案處理完畢,可以用來分析記憶體洩露情況了。
5.開啟MAT:
在Eclipse中點選Windows->Open Perspective->Other->Memory Analysis
6.匯入.hprof檔案
在MAT中點選 File->Open File,瀏覽到剛剛轉換而得到的.hprof檔案,並Cancel掉自動生成報告,點選Dominator Tree,並按Package分組,選擇自己所定義的Package 類點右鍵,在彈出選單中選擇List objects->With incoming references。
這時會列出所有可疑類,右鍵點選某一項,並選擇Path to GC Roots->exclude weak/soft references,會進一步篩選出跟程式相關的所有有記憶體洩露的類。據此,可以追蹤到程式碼中的某一個產生洩露的類。