MAT內(nèi)存泄露分析(一)

使用android shell命令查看內(nèi)存使用情況

使用adb shell dumpsys meminfo pkgname或者直接使用AndroidStudio里面的memory usage功能然后就會(huì)出現(xiàn)如下信息:

Applications Memory Usage (kB):
Uptime: 14237237 Realtime: 23790474

** MEMINFO in pid 8071 [com.xtc.watch] **
                   Pss  Private  Private  Swapped     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap        0        0        0        0    21924     8558     6405
  Dalvik Heap   122472   122372        0    15672   143308    65400    77908
 Dalvik Other    10361    10076      164      224
        Stack      440      440        0        8
    Other dev        4        0        4        0
     .so mmap     6441     3452     2636     2048
    .apk mmap      611        0      340        0
    .ttf mmap      538        0      504        0
    .dex mmap     8407     1640     2940       40
   Other mmap       80        4        0        0
      Unknown    10940    10936        0      148
        TOTAL   160294   148920     6588    18140   165232    73958    84313

 Objects
               Views:      288         ViewRootImpl:        2
         AppContexts:       11           Activities:        2
              Assets:        5        AssetManagers:        5
       Local Binders:       30        Proxy Binders:       38
    Death Recipients:        3
     OpenSSL Sockets:        1

 SQL
         MEMORY_USED:      138
  PAGECACHE_OVERFLOW:       24          MALLOC_SIZE:       62

 DATABASES
      pgsz     dbsz   Lookaside(b)          cache  Dbname
         4       20            306       25/47/12  /data/data/com.xtc.watch/databases/upload.db
  • Native Heap是native層的內(nèi)存堆棧,Dalvik Heap是java層的內(nèi)存堆棧,如果這二者加起來的內(nèi)存占用超過了應(yīng)用最大內(nèi)存限制就會(huì)報(bào)OOM異常,剩下的.so mmap是 C 庫代碼占用的內(nèi)存,.jar mmap是Java 文件代碼占用的內(nèi)存 ,.apk mmap是apk代碼占用的內(nèi)存,.dex mmap是Dex 文件代碼占用的內(nèi)存
  • Objects中的Activities表示當(dāng)前內(nèi)存中的activity對象的個(gè)數(shù),啟動(dòng)一個(gè)activity就會(huì)生成一個(gè)activity對象,當(dāng)退出activity的時(shí)候,activity對象就會(huì)被釋放,所以反復(fù)的進(jìn)出一個(gè)activity界面然后查看Activities的個(gè)數(shù)有沒有保持不變,如果增加了,那么就說明這個(gè)activity對象沒有被釋放,也就是說可能存在內(nèi)存泄漏,但是具體哪里泄漏了并不知道

DDMS查看內(nèi)存使用情況

eclipse中有一個(gè)ddms工具,可以查看線程信息(Threads),內(nèi)存使用情況(VM Heap),內(nèi)存分配跟蹤(Allocation Tracker),CUP使用情況(Sysinfo CUP load),內(nèi)存使用餅狀圖(Sysinfo Memory usage),這里我們暫時(shí)用到VM Heap,選擇要查看的app進(jìn)程,點(diǎn)擊左上角的show heap updates,選擇VM Heap并點(diǎn)擊Cause GC按鈕,然后就出現(xiàn)下圖:


這里寫圖片描述
這里寫圖片描述

觀察data object的Total Size選項(xiàng),這個(gè)是app的創(chuàng)建的java對象做占用的內(nèi)存大小,Count是總內(nèi)存的對象的個(gè)數(shù),反復(fù)的進(jìn)出一個(gè)activity,看data object的Total Size有沒有明顯的增加,正常情況下進(jìn)入一個(gè)activity的時(shí)候會(huì)明顯增加,退出一個(gè)activity會(huì)有明顯的回落,總體是維持在一個(gè)比較穩(wěn)定的水平如果反復(fù)進(jìn)出activity,Total Size不斷上升,那么可能就存在內(nèi)存泄漏了,需要具體排查

MAT分析內(nèi)存泄漏,用AndroidStudio的Monitors的Memory

  • 多點(diǎn)擊幾下Initiate GC來回收一下可被釋放的java對象,因?yàn)閖ava的GC是定期有條件執(zhí)行的,當(dāng)內(nèi)存中只存在很少的無用對象,這時(shí)候可能并不會(huì)觸發(fā)GC,所以手動(dòng)觸發(fā)GC來保證開始檢測內(nèi)存的時(shí)候內(nèi)存都是最干凈的
  • 點(diǎn)擊Dump Java Heap,然后過一會(huì)兒就會(huì)出現(xiàn)一份數(shù)據(jù)分析文件,這時(shí)候的這份數(shù)據(jù)文件是剛開始的程序?qū)ο髢?nèi)存占用情況,接下去就針對一個(gè)activity反復(fù)操作進(jìn)出等等各種反復(fù)操作,覺得差不多了,這時(shí)候就再次瘋狂點(diǎn)擊Initiate GC回收一下可是放的對象,點(diǎn)擊Dump Java Heap,這時(shí)候生成的數(shù)據(jù)分析文件就是經(jīng)過你瘋狂操作后的內(nèi)存占用情況了


    這里寫圖片描述
    這里寫圖片描述

    生成的上述兩個(gè)文件右鍵,點(diǎn)擊Export to Standar .hprof導(dǎo)出到一個(gè)自己指定的目錄文件夾

  • 去官網(wǎng)上面下載MAT來打開這兩個(gè)文件開始內(nèi)存分析


    這里寫圖片描述
    這里寫圖片描述

    上圖中Problem Suspect部分是代表可能存在內(nèi)存泄漏的地方,Remainder表示正常的部分,再繼續(xù)往下看


    這里寫圖片描述
    這里寫圖片描述

    這里寫圖片描述
    這里寫圖片描述

    上面就是對可能存在內(nèi)存泄漏部分的代碼的一個(gè)詳細(xì)的信息,可以看到有些byte數(shù)組占用了大量的內(nèi)存,keywords也是byte[],第二張圖的DexCache可能占用的較多的內(nèi)存,再點(diǎn)擊
    這里寫圖片描述
    這里寫圖片描述

    紅色部分就會(huì)出現(xiàn)一些對象的信息列表:


    這里寫圖片描述
    這里寫圖片描述

    可以輸入正則表達(dá)式來篩選你想要的類,包名下所有的類,Objects是對象的個(gè)數(shù),Shallow Heap是當(dāng)前對象所占用的內(nèi)存大小,不包括對象內(nèi)包含的對象的大小,Retained Heap表示當(dāng)前對象包括對象內(nèi)的子對象一共占用的內(nèi)存大小,所以Retained Heap會(huì)比Shallow Heap大得多,對于一些我們已知的對象在內(nèi)存不泄露的情況下,該對象的個(gè)數(shù)是確定的,所以可以通過分析Objects的個(gè)數(shù)來確定對象是否存在內(nèi)存泄漏,例如同一個(gè)Activity對象在反復(fù)進(jìn)出該Activity5次之后Objects的值為5,那就有問題了,說明同一個(gè)Activity創(chuàng)建了5個(gè)對象,正常情況下應(yīng)該是退出Activity后,對象會(huì)被回收的,所以O(shè)bjects的值應(yīng)該是0才對,而有些對象的Objects不為0并不代表一定存在內(nèi)存泄漏,例如ConnectionService是一個(gè)常駐的Service,那么它是不會(huì)被GC的,而ConnectionService里面的對象可不會(huì)被回收,所以這些對象的Objects值不為0其實(shí)就是正常的了,至于Shallow Heap和Retained Heap,我覺得可以用來分析一些對象的內(nèi)存占用,Shallow Heap一般情況下不會(huì)很大,當(dāng)你發(fā)現(xiàn)Retained Heap非常大的時(shí)候,那就說明該對象里面的對象可能占用了大量的內(nèi)存,可能存在問題;在用Objects找到可能存在內(nèi)存泄漏的對象后,右鍵List Objects,然后有兩個(gè)選項(xiàng):with outgoing references(表示的是當(dāng)前對象,引用了外部引用)和with incoming references(表示的是當(dāng)前查看的對象,被外部引用),一般當(dāng)前對象泄漏了就是對象還被外部對象持有引用,無法被釋放,所以我們選擇查看with incoming references
    這里寫圖片描述
    這里寫圖片描述
    ,點(diǎn)擊with incoming references后
    這里寫圖片描述
    這里寫圖片描述

    可以看到TaskExecutor對象被外部的兩個(gè)對象所引用到了,并且可以看到引用的路徑,右鍵TaskExecutor,選擇Path to GC Roots或者或者M(jìn)erge Shortest Paths to GC Roots選項(xiàng),再選擇exclude all phantom/weak/soft .ect references排除所有的弱引用,軟引用對象尋找看有沒有存在GC Roots,如果沒有那就說明這個(gè)對象不存在內(nèi)存泄漏,最終是會(huì)被GC回收,如果存在GC Roots
    這里寫圖片描述
    這里寫圖片描述

    在繼續(xù)查看GC Roots的對象是什么,其實(shí)就是被哪一個(gè)對象所引用了,上圖可以查看到被外部的sendTaskExecutor對象所引用了而sendTaskExecutor是在ConnectionService這個(gè)常駐Service中的,所以理論上是不應(yīng)該被回收的,所以這里不算是內(nèi)存泄漏,假如sendTaskExecutor是一個(gè)Activity里面的字段,而此時(shí)Activity已經(jīng)退出了,那么這時(shí)候就屬于內(nèi)存泄漏了,因?yàn)锳ctivity退出后,Activity資源包括里面的對象應(yīng)該是被回收掉的,那就找到對應(yīng)的代碼去具體分析可能造成內(nèi)存泄漏的問題所在了,這里明確一點(diǎn),存在GC Roots的不一定就一定存在內(nèi)存泄漏,GC是不會(huì)回收GC Root或者被GC Root所引用的對象的,java對象內(nèi)存泄漏其實(shí)就是對象在不用的時(shí)候仍然被其他對象持有引用導(dǎo)致GC無法回收
  • 比較反復(fù)操作前的內(nèi)存信息和反復(fù)操作后的內(nèi)存信息分析前后有哪些不同
    這里寫圖片描述
    這里寫圖片描述

    ,點(diǎn)擊紅色部分后會(huì)出現(xiàn)下圖


    這里寫圖片描述
    這里寫圖片描述

    可以看到對比后的Objects和Shallow Heap信息,看Objects表示前后兩種內(nèi)存信息對于同一個(gè)對象是否有個(gè)數(shù)上的增加,因?yàn)槿绻麑ο竽鼙徽;厥?,那么開始操作前是0,操作后經(jīng)過GC應(yīng)該也是0,如果個(gè)數(shù)增加了,那就表示這個(gè)對象可能存在內(nèi)存泄漏了,拖動(dòng)到底部可看到總的比較情況
    這里寫圖片描述
    這里寫圖片描述

    上圖可知,操作前的app的Objects總數(shù)比操作后的Objects少了1個(gè),同時(shí)可以看到是HttpDns這個(gè)對象的問題,于是再切換到操作后的histogram去按照上述步驟查找GC Roots,再具體分析內(nèi)存泄漏的問題

  • MAT當(dāng)中還有一個(gè)dominator tree視圖,具體就不復(fù)述了,可以參考這篇大神文章MAT內(nèi)存分析

網(wǎng)上的開源項(xiàng)目LeakCanary也是分析內(nèi)存泄漏的一種有效方法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容