Android 內存泄漏的簡單檢查與分析方法
導語
內存泄漏問題大約是Android開發者最煩惱的問題之一了,項目中連續遇到幾個內存泄漏問題,這里簡單總結下檢查分析內存泄漏的一些工具與方法。
一、什么是內存泄漏?
大家都知道,java是有垃圾回收機制的,這使得java程序員比C++程序員輕松了許多,存儲申請了,不用心心念念要加一句釋放,java虛擬機會派出一些回收線程兢兢業業不定時地回收那些不再被需要的內存空間(注意回收的不是對象本身,而是對象占據的內存空間)。
Q1:什么叫不再被需要的內存空間?
答:Java沒有指針,全憑引用來和對象進行關聯,通過引用來操作對象。如果一個對象沒有與任何引用關聯,那么這個對象也就不太可能被使用到了,回收器便是把這些“無任何引用的對象”作為目標,回收了它們占據的內存空間。
Q2:如何分辨為對象無引用?
答:2種方法
-
引用計數法
直接計數,簡單高效,Python便是采用該方法。但是如果出現 兩個對象相互引用,即使它們都無法被外界訪問到,計數器不為0它們也始終不會被回收。為了解決該問題,java采用的是b方法。
-
可達性分析法
這個方法設置了一系列的“GC Roots”對象作為索引起點,如果一個對象 與起點對象之間均無可達路徑,那么這個不可達的對象就會成為回收對象。這種方法處理 兩個對象相互引用的問題,如果兩個對象均沒有外部引用,會被判斷為不可達對象進而被回收(如下圖)。
Q3:有了回收機制,放心大膽用不會有內存泄漏?
答:答案當然是No!
雖然垃圾回收器會幫我們干掉大部分無用的內存空間,但是對于還保持著引用,但邏輯上已經不會再用到的對象,垃圾回收器不會回收它們。這些對象積累在內存中,直到程序結束,就是我們所說的“內存泄漏”。
當然了,用戶對單次的內存泄漏并沒有什么感知,但當泄漏積累到內存都被消耗完,就會導致卡頓,崩潰。
二、發現內存泄漏
內存泄漏不可小視,在Android開發中,比如說一個Activity頁面會占用許多資源開銷,如果頁面發生泄漏,關閉以后頁面沒有能被系統回收,對應用程序的傷害是很大的。
Q1:在Android開發測試中一般如何發現內存泄漏的發生呢?
答: 方法1:反復操作觀察內存變化
內存泄漏常見變現為程序使用時間越長,內存占用越多。那我們通過反復操作應用,比如反復點開/關閉頁面,觀察內存變化狀況是否一點點上漲,可以粗略地判斷是否有內存泄漏
1.通過 DDMS 中的 heap 工具,可以查看應用內存的使用情況
2.Android studio也可以方便查看
方法2:通過代碼檢測Activity泄漏
基本思路:
1)debug版本可以起一個長期工作的線程LeakThread在后臺專門做泄漏檢測
2)向Application注冊一個 頁面生命周期 的監聽:application.registerActivityLifecycleCallbacks
3)在監聽類中對 onActivityDestoryed(Activity activity) 的事件回調做處理:
如果一個Activity走到onDestroy,那么這個Activity對象就是需要被回收的目標。
我們聲明一個檢測對象的弱引用ref = new WeakReference (activity)。
PS:與強引用和軟引用相比,弱引用不會被回收器當做一個“有效”的引用,不會影響其引用對象的釋放。實際上,垃圾回收器會毫不猶豫地回收只有弱引用的對象~
4)在 LeakThread中我們每隔一段時間檢測一下ref.get() 是否為空,為空說明activity已被釋放。不為空可以手動觸一次發gc;如果超過一段時間,比如50s,頁面對象還未被清理,我們可以推斷內存泄漏的發生.
5)當內存泄漏發生時,提示給開發者,并自動dump出.prof文件。
因為代碼檢測不是這里的重點,代碼就不貼了,只記思路。
三、分析內存泄漏(DDMS dump + MAT分析)
發現可能出現內存泄漏時,我們需要對.prof文件進行分析,方能快速定位到是哪個倒霉家伙導致了內存泄漏
3.1、如何dump出.prof文件?(可參照前文圖片)
-
打開DDMS ,Eclipse 可以切到DDMS視圖,Android studio可以從Tools-Android-Android device monitor進入DDMS
-
找到app的進程,在進程上方點擊“update heap”按鈕,可以先主動出發一次GC,待內存占用數據稍微穩定下來后 點擊“Dump HProf File”,便可以導出.prof文件
3.2:導出.prof文件后如何分析?
Android studio可以直接打開prof文件。點開Analyzer Tasks的面板,點擊右上角的開始按鈕。
分析完成后,發生內存泄漏的頁面對象會出現在Analysis Results面板-Leak Activityes的目錄下。
如圖,原來泄漏發生是LoadingRoomActivity的鍋!
3.3 進一步分析泄漏的原因,你會需要一個好用的內存分析工具:MAT
在官網可以下載到它:
http://www.eclipse.org/mat/downloads.php
雖然MAT不會準確告訴你你的代碼哪泄漏了,但是它會給你發現哪泄露的數據和線索。
3.3.1 打開.hprof前可能遇到的問題:
在MAT中打開.prof頁面,你可能會遇到一點小挫折:
如上圖,可能會彈出 ‘Parsing heap dump from xxx has encountered a proplem’ 的錯誤彈窗
這是因為文件版本和編輯器能支持的版本有沖突的原因。
解決方案如下:
Sdk安裝目錄下platform-tools里有一個hprof-conv工具可以解決該問題。在cmd控制臺執行:
hprof-conv input.hprof output.hprof
重新再MAT打開output.hprof 就可以打開了~
值得一提的是,如果你dump出的文件太大的話,也有可能發現打不開的現象,這時候,打開安裝MAT目錄下的MemoryAnalyzer.ini 把-XmX改大些重啟即可。但是也不要改得比你機器的可用內存還大,不能太貪心哈哈~
3.3.2 打開.phrof文件后的分析
通過MAT打開.phrof文件后,會彈出Overview 和 Leak Suspects 2個標簽頁。
Leak Suspects標簽頁可見如下圖:
Leak Suspects視圖展示了app內存占用的比例,淺色是空閑的內存,其他是內存占用的空間。每塊內存對應的問題也都列在下面。點開每個Problem Suspect下的details,可以看到有哪些類的實例占用了內存和占用大小等信息~
此時我們已經有了懷疑的目標,為了更清晰地查看,我們可以回到Overview頁面,打開Histogram頁面:
在打開的Histogram標簽頁中,我們填入檢測對象,在列出的匹配項中過濾掉對象的非強引用。
到這里我們就可以看到,是哪個壞蛋hold住了你的對象了。MAT能夠給到的支持也就到這里,接下來,還是需要你根據這些線索到代碼中尋找判別和修正了~``
來自:http://mp.weixin.qq.com/s?__biz=MzAxMzYyNDkyNA==&mid=2651332518&idx=1&sn=bcc31ed271efbdc7784c2b18bd046d33&scene=4#wechat_redirect