Android 應用內存泄漏分析(實戰篇)

 在Android應用開發中,內存泄漏比較常見;如果應用越來越卡頓以致被系統殺掉,這個時候就可靠考慮分析下是否是內存泄漏導致的。在此分享內存泄漏分析的一種方式,主要是通過ADB、MAT工具分析,並簡單描述內存泄漏的原因。

工具使用

 分析應用內存,首先查看應用內存使用情況,可以通過Android Studio Profiler 或者dumpsys meminfo 命令查看,然後再通過內存分析工具分析內存泄漏情況。Android 內存泄漏分析工具主要有兩種:1,通過Android Stuido Profiler工具實時查看內存使用情況;2,獲取應用的hprof文件,然後通過MAT工具分析。我們今天要介紹的就是第二種方式;

查看內存使用情況

 Android Studio Profiler查看內存比較直觀,但這次我們主要看命令的方式,因爲Android Studio Profiler無法應用於Android5.0以下的機器。比如查看A應用的內存使用情況,使用"dumpsys meminfo A應用包名"
memory_am_dumpsys_meminfo

  • 私有內存(Dirty and Clean):
    進程獨佔內存,也就是進程銷燬時可以回收的內存容量。通常private Dirty內存是最重要的部分,因爲只被自己進程使用,這也是我們需要關注的。Dirty內存是已經被修改的內存頁,因此必須常駐內存(因爲沒有swap);Clean內存是已經映射持久文件使用的內存頁(例如正在被執行的代碼),因此一段時間不使用的話就可以置換出去。

  • 實際使用內存(PSS):
    將跨進程共享頁也加入進來, 進行按比例計算PSS。這樣能夠比較準確的表示進程佔用的實際物理內存。

當我們操作應用的時候如果發現TOTAl Private Dirty內存只增無減,那就很有可能存在內存泄漏,需要將應用的堆內存dump出來。

獲取hprof文件

 獲取hprof文件主要包含兩個步驟:

  • dump 出java heap 文件,通過Android Studio Profiler 工具或者“am dumpheap”命令導出java heap文件

    root@M1AEV-MY2:/ # am dumpheap 應用包名 /sdcard/xxx.hprof
    
  • 將dump出的java heap 文件通過hprof-conv 轉化成MAT工具可識別的hprof文件;

    hprof-conv E:\zhuzp\log\xxx.hprof E:\zhuzp\log\memoryleak\xxx.hprof
    

    通過上面兩個步驟就得到了MAT可識別的hprof文件,接下來就是開始通過MAT工具分析

MAT工具

 MAT(Memory Analyzer Tool)是一款快速且功能豐富的Java堆內存分析工具,它能幫助你發現內存泄漏問題以減少內存消耗。

 通過MAT工具打開一個hprof文件,進入概述圖界面顯示如下:
在這裏插入圖片描述

OverView 界面有以下幾類:

  • Details

    Size: 66 MB Classes: 4.7k Objects: 806.1k Class Loader: 5 Unreachable Objects Histogram
    

    Java heap 內存總共 66M,有4700個類,806100個對象,5個ClassLoader,前面的這些內容都很明瞭。唯一需要注意的是Ureachable Objects Histogram,這是顯示GC不可達對象,也就是會被GC回收的對象柱狀圖。

  • Biggest Objects by Retained Size

    這一塊比較直觀的顯示出內存佔用最大的對象,通過將鼠標在餅圖上移動可以看到各個對象,比如將鼠標放在最大塊上時顯示出的就是RadioDataObservable
    memory_biggest_object

  • Actions

    Actions下有幾個比較重要的內容:Histogram,顯示每個類對象的個數;Dominator Tree 支配節點樹,顯示最大的對象和它所keep alive 的對象。

  • Reports

    Reports 下面的兩個內容也比較重要:Leak Suspect 可能存在內存泄漏的地方;Top Component 內存佔用超過總數1%的對象;

  • Step by Step 這可以理解成基本使用教程

 在瞭解基本界面後,看幾個比較重要且常用的功能界面;

Leak Suspect

  當打開hprof文件時默認是進入該界面,該界面會顯示可能存在內存泄漏的地方。如果分析內存泄漏,主要也是從這個界面開始分析。比如下面圖中就顯示了兩處可能存在內存泄漏的地方:1,RadioDataObservable;2,Bitmap;
memory_leak_suspect
  通過點擊Details進入內存泄漏詳細界面,這裏只需要關注Accumulated Objects in Dominator Tree,這個可以理解爲堆積對象的支配節點樹(也就是內存泄漏對象持有的引用關係)。Shortest Path To the Accumulation Point,可以理解爲內存泄漏對象被哪些最短引用鏈對象所引用。
memory_leak_detials
點擊圖片中的標註,進入支配節點樹界面,這裏列舉了Object數據包含了哪些對象,可以看到都是SubRadioFragment對象,並且在最下面顯示了對象的總數。也就是說RadioDataObserverable中的ArrayList集合中存在266個SubRadioFragment對象。
memory_domiator_tree
 到這裏基本就能定位內存泄漏的原因了,很有可能就是一直往集合裏面添加對象,沒有移除對象。通過追溯代碼也確實是因爲只在Fragment onCreateView()時往DataObservable中添加Observer,在onDestroyView()沒有將Observer移除。

 通過MAT工具分析內存泄漏到這一步,其實也基本結束了。不過再瞭解Histogram、Dominator Tree、Top Consumers對我們應用的內存優化也有一定的幫助。

Histogram

 柱狀圖顯示的是每個類實例的個數,在此界面可以找出哪些類實例佔用內存比較大。
memory_histogram
首先需要明白Retained Heap和Shallow Heap。Shallow Heap是對象本身佔據的內存的大小,不包含其引用的對象。Retained Heap是當前對象大小+當前對象可直接或間接引用到的對象的大小總和,並且排除被GC Roots直接引用的對象
memory_gc_root
如上圖所示,對象A的Shallow Heap大小等於Retained Heap,因爲對象A沒有引用其他對象。對象B的Retained heap大小等於對象B、對象D、對象E和對象F(但不包括對象C) Shallow heap總和。

在Histogram界面任意右鍵一個類,顯示如下:memory_histogram_list

這個地方關注項內容

  • List objects 顯示所有對象,包括兩種方式;with outgoing references 是指以對象持有哪些引用的形式顯示出來所有對象;with incoming references 是指以對象被引用的關係鍊形式顯示出所以對象;

memory_outgoing_references
memory_incoming_references

  • Merge Shortest Paths to GC Roots 對象到GC Roots 的最短路徑

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KdynwjGC-1574424861660)(./memory_shortest_gc_roots.png)]memory_shortest_gc_roots

    在這可以選擇過濾不同的引用類型,常用的就是過濾所有幽靈引用、弱引用、軟引用只查看強引用,因爲其他引用對象都能夠被GC回收。從下圖可以看到RadioFragmnet對象的GC Roots 對象就是靜態變量sRadioDataObservable;
    memory_merge_shortest_gc_roots

原理

  上面我們講的是分析內存泄漏的方式,那到底是什麼原因導致內存泄漏的呢。本該被回收的對象,但是由於編碼不當導致對象沒有被回收。 不同的GC算法在回收對象前都會判斷對象是否是活動對象,判斷活動對象的方式是判斷該對象和“GC Roots對象”是否存在引用鏈。有關內容可以參考《Android內存泄漏原理及優化(原理篇)》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章