在介紹如何 debug 之前,首先必須知道的是,目前的系統到底有沒有記憶體洩漏?以下先介紹幾個常用的記憶體觀察法:

  • cat /proc/meminfo      // 粗略的觀察目前記憶體的種類以及各個種類的大小
  • ps                             // 觀察每個 process 目前記憶體使用的情況 
  • top                            // 看看前幾個記憶體使用最多的 process 是哪些
  • dumpsys meminfo      // Android 特有的指令,有更多的資訊







方法1 Memory Analyzer :

這個方法必須配合 Eclipse 使用,因為 Memory Analyzer (MAT) 是 Eclipse 的一個 Plugin。
  1. 第一個步驟當然是安裝 MAT,安裝得方法有許多種,你可以下載MemoryAnalyzer-1.0.1.201008091353.zip,解開到Plugins,或者在Eclipse上點選 Help --> Install New Software... --> Add,然後在 Name 輸入 Memory Analyzer ,在 Location 輸入 http://download.eclipse.org/mat/1.0/update-site/ ,如圖所示:
    按下 OK,之後要選擇安裝 Plugin 完成安裝程序。

  2. 一如往常的執行你想要 debug 的 Android 程式,然後透過進入 shell 裡面檢查 /data/misc/ 這個目錄是否有可讀寫的權限,因為我們等一下會 dump file 到這個目錄。另外,也可以簡單的把他的權限設成 777:
    adb shell
    chmod 777 /data/misc/
    

  3. 接下來執行程式到你認為會產生 leak 的地方,來回多執行幾次,然後利用 kill 這個指令送信號 SIGUSR1 (-10) 給你的 process (可以透過 ps 查看 Process ID),假設你要 debug "com.test.debug" 這個 process :
    ps
    ...
    PID UID       VSZ  Stat Command
    100 1000   255255    S  com.test.debug
    ...
    
    kill -10 100
    

  4. 你可以透過 logcat 來確認你的 process 有沒有收到 signal:
    logcat &
    

    如果看到類似如下的訊息表示有收到:
    I/dalvikvm(  236): threadid=3: reacting to signal 10
    I/dalvikvm(  236): SIGUSR1 forcing GC and HPROF dump
    I/dalvikvm(  236): hprof: dumping VM heap to "/data/misc/heap-dump-tm1291081439-pid100.hprof-hptemp".
    I/dalvikvm(  236): hprof: dumping heap strings to "/data/misc/heap-dump-tm1291081439-pid100.hprof".
    I/dalvikvm(  236): hprof: heap dump completed, temp file removed
    D/dalvikvm(  236): GC_HPROF_DUMP_HEAP freed 585 objects / 60000 bytes in 10595ms
    

    查看一下 /data/misc/ 目錄應該可以看到 heap-dump-tm1291081439-pid100.hprof:
    ls /data/misc/
    ...
    heap-dump-tm1291081439-pid100.hprof
    ...
    

  5. 離開 shell
    exit
    

    透過 adb pull 把這個檔案抓下來。
    adb pull /data/misc/heap-dump-tm1291081439-pid100.hprof
    

  6. 透過 hprof-conv 把剛剛抓下來的檔案轉換成 Memory Analyzer 認識的格式:
    hprof-conv heap-dump-tm1291081439-pid100.hprof debug.hprof
    

  7. 開啟 Eclipse,點選 Window --> Open Perspective --> Other... ,然後選擇 Memory Analyer。

  8. 點選 File --> Open Heap Dump... ,然後選擇剛剛轉換過的檔案 (此例為 debug.hprof)。接著會出現一個對話框,詢問你想產生什麼樣的報告,如下圖:
    此時我們的目的是偵測 memory leak,所以自然是選擇 Leak Suspects Report,按下 Finish 後,就會產生一個報告,告訴你可疑的地方。是不是很棒呢!


方法2 libc_debug.so : 

這個方法基本上是將 libc.so 換成 libc_debug.so 利用 libc_debug.so 提供多項偵錯的功能來查看記事體是否被不當的使用。

  1. 將 /system/lib/libc.so 替換成 /system/lib/libc_debug.so
  2. 重啟系統 adb shell reboot 
  3. adb shell setprop libc.debug.malloc 1" (or 5, or 10 for slightly different behaviors)
  4. 新增 "native=true" 到你的 ~/.android/ddms.cfg 檔案中
  5. 啟動 stand-alone 版的 DDMS. 接著你應該會看到有 "Native Heap" tab 出現,切到這個頁面後就可以 native memory 的使用情況。


方法3 c/c++ debugger : 

利用c/c++ debugger 來協助 debug c/c++ 層的 memory leak,接下的例子會使用 dmalloc 來 debug JNI native library.

  1. 先到 dmalloc 的官方網站 http://www.dmalloc.com 下載 source code。
  2. 交叉編譯 dmalloc,我個人是偏好用 Android NDK 來編譯,不過在這之前必須要先產生原本的 Makefile 然後照著 Makefile 來產生 Android.mk
    1. 執行 configure
      ./configure --prefix=/arm-linux/ --enable-cxx --enable-threads
      
      其中/arm-linux/是隨便指定的路徑,反正我們只是想要參考 Makefile。
    2. 參考 Makefile,轉換成 Android.mk
    3. 執行 ndk-build 來編譯 dmalloc
  3. 因為我們是要 debug JNI 的 native library 所以沒辦法透過一般的設定環境變倏地方法來指定 debug options:
    DMALLOC_OPTIONS=debug=0x4e48503,inter=100,log=/sdcard/dmalloc.%p
    export DMALLOC_OPTIONS
    

    必須自行在程式進入點指定 debug option,以 JNI 為例,可以利用 JNI_OnLoad() 當作是進入點,此外,離開的時候也必須關閉:
    #include "dmalloc.h"
    
    JNIEXPORT jint JNICALL JNI_OnLoad (
    ) {
        dmalloc_debug_setup ("debug=0x4e48503,inter=100,log=/sdcard/dmalloc.%p");
    }
    
    JNIEXPORT void JNICALL JNI_OnUnLoad (
    ) {
        dmalloc_shutdown ();
    }
    

    然後利用 dmalloc_mark() 與 dmalloc_log_changed() 來標記要偵錯的區段,下面我們以一個 JNI function 為例,故意造成 memory leak:
    unsigned long mark;
    
    JNIEXPORT void JNICALL JNI_com_example_debug_Test (
        JNIEnv* env,
        jobject thisArg
    ) {
        // Mark the start of debug
        mark = dmalloc_mark();
    
        // Leak memory 1000 bytes.
        char* leak = (char*) malloc (1000);
    
        // log unfreed pointers that have been added to
        // the heap since mark
        dmalloc_log_changed (
            mark,
            1,    // log unfreed pointers
            0,    // do not log freed pointers
            1     // log each pnt otherwise summary
        );
    }
    

    一旦這個 function 被呼叫,就會在 /sdcard/ 目錄下產生類似 dmalloc.1000 (副檔名是這個process 的 PID),然後裡面就會告訴你那一行發生 memory leak了。
    948436802: 1: Dmalloc version '5.5.2' from 'http://dmalloc.com/'
    948436802: 1: flags = 0x4e48503, logfile 'logfile'
    948436802: 1: interval = 100, addr = 0, seen # = 0, limit = 0
    948436802: 1: starting time = 948436802
    948436802: 1: process pid = 1000
    ...
    

4 意見:

I have a question,
How to convert the makefile of dmalloc to Andriod.mk.....
Cause I wanna test in ARM-linux...
Can you send me a file?

That's my email leon.lyn@hotmail.com
Can you tell me how I can contact you man?

Thank you very much.

Sorry for the late respone, I was busy recently. You can download Android.mk in the following URL https://docs.google.com/leaf?id=0B1tl3M-UqOyTNzFjOWU4NDgtZjg2NS00MGNjLWJiOGItMmRlZDE1M2IyYjU2&hl=zh_TW&authkey=CNCchJMC

Good Luck!

Long time no see,I am also busy in china.
Thank you very much.
I will try to do that.
Good luck for me.

Thanks Again.

Thank you for your suggestions.
I am also busy in china now.
I will try to do that.
Good luck for me.
Thanks again.