1. 程式人生 > >Android Studio + MAT 給你看真實專案實戰的記憶體洩露分析

Android Studio + MAT 給你看真實專案實戰的記憶體洩露分析

這裡寫圖片描述 這裡寫圖片描述 這裡寫圖片描述
先貼出我要進行實戰的專案背景;專案已經出爐快半年時間了,現在要對它進行效能上的優化,這時候就要使用到 MAT 。然後隨便記錄一下我的分析歷程。

首先要了解兩個概念:記憶體溢位和記憶體洩漏
記憶體溢位(out of memory),是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是記憶體溢位。
記憶體洩露(memory leak),是指程式在申請記憶體後,完事兒之後,仍被其他物件持有其引用,而無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重,最終會導致記憶體溢位。

分析記憶體溢位問題,首先我們要非常清楚怎麼去查詢導致記憶體空間溢位的問題的操作!!!
就使用MAT 來講,如果你從A頁面進入B頁面在進入C頁面。然後你會在Monitors中看到記憶體遞增,覺得不正常。下意識中想要分析一下B和C頁面的一些程式碼邏輯是否出現了記憶體溢位。此時你卻在C頁面就一直瘋狂的GC,然後再拿到.hprof檔案進行分析。這種操作是錯誤的。為什麼?因為你要分析的是應用的B和C頁面可能出現的記憶體洩漏,真正的分析記憶體洩漏的關鍵操作是,你至少要逐次關閉了C和B頁面。然後GC,再然後拿到.hprof檔案去進行分析。
總結語:也就是說,查詢記憶體洩露是要在一個物件在你認為中已經被釋放了,並且你也這樣操作了(比如關閉了頁面,停止了執行緒)。但是在分析時卻發現,這個狗日的物件還活著,且被別的物件持有者引用。這個時候的分析才算是到了時機。

先挑選兩個頁面進入並頻刷,得記憶體分析,看圖
這裡寫圖片描述

操作過程
這裡寫圖片描述
此時,我就開啟應用,在首頁和由首頁進入的第二級頁面進行頻刷,然後關閉回到首頁面。看到記憶體呈現階梯狀增漲,並伴有記憶體抖動現象。然後開始操作,點選Initate GC 按鈕 釋放記憶體,經過3秒鐘左右看下記憶體狀態顯示,再次GC 然後點選Dimp Java Heap 按鈕。然後,就會看到這樣的效果:
注意:一定要手動點選 Initiate GC按鈕手動觸發GC,這樣抓到的記憶體使用情況才是不包括Unreachable物件的。Unreachable指的是可以被垃圾回收器回收的物件,但是由於沒有GC發生,沒有釋放,這時抓的Unreachable

就存在正在使用的記憶體中。
這裡寫圖片描述
於是就會看到 Java Heap 檔案,點選Analyzer Tasks 按鈕,呈現分析視窗。並執行Perform Analysis就能對我們剛才進行GC 的那一段記憶體狀態進行分析,並呈現出分析的結果。
這裡寫圖片描述
由圖片我們看到了分析結果Analysis Results 出現Leaked Activities 記憶體洩漏的根源竟然是啟動頁面 SplashAty 這個Activity頁面。
接下來還要繼續深入分析記憶體洩漏的原因
Captures 視窗右擊剛Dump Java Heap 匯出標準的.hprof 檔案,匯出到任意位置並使用基於EclipseMAT 工具開啟。 然後可以看見初始開啟的頁面結果
這裡寫圖片描述
EclipaseMAT 顯示的頁面點選 Histogram 顯示
這裡寫圖片描述
然後輸入您需要定位的Activity頁面的名字,檢視該Activity所發生的記憶體洩漏的源頭頁面
這裡寫圖片描述
或者點選QQL圖示 輸入 select * from instanceof android.app.Activity ,然後點選紅色歎號執行,結果是這樣的
這裡寫圖片描述
選中ta並作這樣的操作,就能夠看到具體發生記憶體洩漏的位置
這裡寫圖片描述
最後得到這樣的結果
這裡寫圖片描述
然後結合程式碼分析並解決問題!!至於還有很多可做的一些操作,那就需要自己去發現。
接下來,做一些概念上的介紹。畢竟只會操作還是遠遠不夠的,邏輯會很複雜,洩露也會頻發,查詢也會變得千奇百怪。只有全方位的瞭解了MAT 才能做到有備無患,仗劍走天涯!

Histogram

  • 列出記憶體中的物件,物件的個數以及大小。

Dominator Tree

  • 列出最大的物件以及其依賴存活的Object (大小是以Retained Heap為標準排序的)。

Top Consumers

  • 通過圖形列出最大的object。

Shallow heap

  • 無序列表1 Shallow size就是物件本身佔用記憶體的大小,不包含其引用的物件。針對非陣列型別的物件,它的大小就是物件與它所有的成員變數大小的總和。當然這裡面還會包括一些java語言特性的資料儲存單元。針對陣列型別的物件,它的大小是陣列元素物件的大小總和。

Retained Heap

  • 它表示如果一個物件被釋放掉,那會因為該物件的釋放而減少引用進而被釋放的所有的物件(包括被遞迴釋放的)所佔用的heap大小。(間接引用的含義:A->B->C, C就是間接引用。不過,釋放的時候還要排除被GC Roots直接或間接引用的物件。他們暫時不會被被當做Garbage)