在日常開發中,不可避免的會碰到內存泄漏問題,本片文章作為記錄自己在工作中如何使用工具發現內存泄漏,定位內存泄漏。
工具:leakcanary庫,Android Profiler工具,MAT工具。
leakcanary庫
leakcanary庫是Square出品的,幫助我們在開發中及時發現程序中內存的內存泄漏問題,例如某個Activity退出時,該Activity內占用的資源應該全部被釋放。但是若有資源被占用未被釋放,就會導致內存泄漏,這時leakcanary會幫我們進行GC Root可達性分析,并將內存泄漏的原因通過通知欄提示用戶。如圖:
點擊右側的 + 號展開詳細日志,然后定位具體的內存泄漏原因,如果通過該日志難以定位。那么接下來就借助 Android Profiler的內存分析。通過leakcanary定位到內存分析發生在 RecodeVoiceWebViewAct頁面,然后只要使用 Android Profiler分析該頁面。
Android Profiler 工具
Android Profiler 是谷歌在Android Studio3.0推出的性能分析工具,取代了之前的 Android Monitor 工具。對于該工具的詳細介紹,參考該文 官方文檔。本文只介紹如何使用它的內存分析工具這塊。首先看一張分析結果圖,然后說明如何檢測該頁面的內存泄漏。
上圖的標注:
(1) 選取的當前的分析的進程。
(2) 手動進行GC
(3) 點擊該按鈕會在左側(7)處生成hprof文件
(4) 選取該段的內存,進行分析。
(5) 顯示該類的實例,下方的Call Stack 顯示該實例分配的線程
(6)顯示需要進行內存分析的類
上文知道,我們已經定位內存泄漏發生在該頁面,然后按照如下步驟進行分析:
打開 Android Profiler 選取app進程,選擇MEMORY該選項。然后對該頁面進行幾次反復的開啟關閉,然后回到上一個頁面時,
點擊(2)按鈕進行 GC ,然后選取 GC 之后的這段內存進行分析。選取這段 GC,是由于該頁面在這段內存中已經關閉,正常來講在這段內存中不會存在該頁面的實例。但是選取這段內存之后,查看實例的分配情況,發現在這段內存中還是存在該頁面的實例。說明該頁面確實發生了內存泄漏,點擊(6)處的該類,在右側顯示實例窗口。點擊實例,可以通過下方的 Call Stack 查看具體內存泄漏的原因。有些原因可能通過經驗可以判斷出來大致的原因,但是有些可能需要進一步通過下一步介紹的 MAT 工具進行準確定位。
MAT工具
MAT 是分析 Java 堆數據的專業工具,用它也可以定位內存泄漏的具體原因。用它之前需要將之前用Android Profiler 分析的那段內存生成一個hprof 格式的文件,但是這個文件不能直接用,需要通過sdk自帶的工具 hprof-conv.exe (sdk/platform-toos下)對Android Studio 生成的hprof 文件轉化成MAT 可以使用的 hprof 文件。在上文的 Android Profiler 分析的結果圖中點擊(3)處的按鈕生成 hprof 文件。然后使用 hprof-conv.exe 對該文件進行轉化。然后用 MAT工具打開轉化后的文件。
-
篩選進行分析的對象
image一般MAT中使用最多的是Histogram選項,點擊圖示指向的按鈕,打開Histogram,然后在表格的第一行輸入需要進行分析的類名稱。
-
右鍵點擊篩選出該選項,顯示菜單,選擇 list objects ->with incoming refs 項。顯示該類的實例
image 右健 該實例,菜單選擇Path to GC Roots-->exclue all phantom/weak/soft etc. reference項。
從上圖可以看出由于存在GCRoot,因此該對象不會被回收,并導致內存泄漏。從結果可以看到 RecodeVoiceWebViewAct的實例被util包下的VoiceEngineHelper類中的一個任務線程持有。因此只要在頁面關閉時,停止該任務線程就可以解決。
總結
上面提供了三種發現定位內存泄漏的工具,對于容易定位的內存泄漏,可以直接使用leakcanary庫以及Android Profiler 定位內存泄漏的地方,對于稍微復雜的需要用MAT工具通過定位具體的內存泄漏的原因。