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
檔案,匯出到任意位置並使用基於Eclipse
的 MAT 工具開啟。 然後可以看見初始開啟的頁面結果 在
Eclipase
的 MAT
顯示的頁面點選 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)