Eclipse MAT -- android 內(nèi)存分析工具
資料
介紹
目前產(chǎn)品中,一直存在內(nèi)存溢出,頻繁崩潰的現(xiàn)象,作為一個門外漢,也需要了解下android 具體的測試方法以及優(yōu)化策略.
在搜索了許多問題之后,我們把問題定位到內(nèi)存溢出. 那么如何監(jiān)控移動設(shè)備(android)的內(nèi)存使用情況呢?
經(jīng)過對比,MAT 在我們場景中已經(jīng)可以作為分析內(nèi)存的工具來使用.
Eclipse MAT
android studio 自帶了android device monitor ,可以提供錄制的hprof文件,并且拖拉到studio 中就可以進行簡單的分析,but .
看不懂~~ 回過頭來 ,還是使用eclipse-mat 來分析內(nèi)存吧 ,畢竟這個資料會多一些.
studio 錄制的 hprof 文件,需要通過SDK 自帶的hprof-conv 進行轉(zhuǎn)換之后才可以導(dǎo)入到eclipse-mat
導(dǎo)入進來之后就需要對eclipse mat 的簡單使用有一個初步的了解.
名詞解釋
- Shallow heap : 對象自身占用的內(nèi)存
- Retained heap: 對象自身占用的內(nèi)存 + 對象直接或間接引用占用的內(nèi)存
- outgoing : 被引用
- ingoing: 引用
- weak reference: 弱引用 ,簡單描述為: 假如一個A 對象依賴一個弱引用的B對象,則,如果A 對象為null ,GC回收時候就會回收A
- soft reference: 軟引用,如果內(nèi)存足夠就不回收,如果不夠就回收,不回收時候該對象可以被程序使用.
- phantom reference: 虛引用, 無論從哪里都無法訪問虛引用所引用的對象,GC時候,直接就finalize,該對象清除時才加入到Reference隊列.
- GC root: 生成一個到gc 根 的引用關(guān)系
- dominator tree: 支配樹
- heap size: 堆大小,當(dāng)前空間不足則系統(tǒng)會自動增加大小
- Allocated: 堆中已分配大小,即app實際占用內(nèi)存大小,資源回收后,該值會變小.
內(nèi)存分析流程
在android 平臺,長期占用一些資源的引用,造成內(nèi)存不能釋放,帶來的問題有很多,
1. Drawable 由于加載Bitmap 導(dǎo)致的問題 ,比如旋轉(zhuǎn)屏幕會銷毀當(dāng)前activity,并創(chuàng)建一個新的activity.
這時候則會重新加載UI視圖和資源.
2. bitmap 圖片過大導(dǎo)致內(nèi)存溢出,編碼上需要options來減小圖片
3. Handler 導(dǎo)致的內(nèi)存溢出.
4. AsyncTask 由于非靜態(tài)匿名內(nèi)部類導(dǎo)致的內(nèi)存溢出
5. Context 上下文的長時間引用導(dǎo)致的內(nèi)存溢出,如果需要緩存context 則需要使用applicationContext代替.
導(dǎo)致內(nèi)存溢出,也就是內(nèi)存中存在的引用關(guān)系得不到清理所導(dǎo)致的,所以,以后在開發(fā)過程中要考慮好對象的應(yīng)用.
那么配合mat 如何才能找出問題呢?
如何判斷內(nèi)存溢出風(fēng)險?
在開啟 android device monitor 之后, 我們監(jiān)控 heap size , allocated 的大小變化,
通過觀察,發(fā)現(xiàn)沒問的應(yīng)用在幾個Activity 切換之后,內(nèi)存有最初的15M 維持到25-40 之間
設(shè)想,是否存在有些對象沒有釋放,導(dǎo)致內(nèi)存一直增加不降低呢?
通過導(dǎo)出的hprof 文件,我們 看看eclipse-mat 中又有哪些記錄呢?
導(dǎo)入之后,我們發(fā)現(xiàn)mat 已經(jīng)發(fā)現(xiàn)了可能產(chǎn)生的問題 Leak Suspects ,當(dāng)然這是mat 懷疑的,
是否真的有問題,我們需要查看下GC root 依賴關(guān)系.
Problem 1: Bitmap 的實例 占用了太多的引用. byte[]的積累已經(jīng)占用了16%.原因是內(nèi)存中 byte[] (可能)導(dǎo)致了問題.
我們通過histogram 發(fā)現(xiàn)排第一的就是byte ,查找下他的引用有哪些(使用list Object) 發(fā)現(xiàn)了被很多個實體引用.
通過sort 我們找?guī)讉€來看看問題,選中一個最大的(Path to GC Roots 去除掉弱引用和軟引用),這里出現(xiàn)了一個引用ROOT樹,
找到有一個GuideRedActivity. 那么接下來查看下這個類,為什么一直在內(nèi)存中. 這個Activity 負責(zé)生成一個圖片遮罩效果.
images.setBackgroundResource(xx);
通過一個點擊事件頻繁調(diào)用了 4次 setbackGroundResource,圖片大小為640*1200 ,通過分析,這里使用setbackgroundResource 確實
存在內(nèi)存泄漏的風(fēng)險.
Problem 2: 40 byte[] 實例占用了大量的內(nèi)存12% . 這個問題和上面的Bitmap 問題應(yīng)該一直,那么我們再來分析一個. 我們通過
以上步驟找到第二個占用內(nèi)存比較大的byte[] ,查詢他的引用,好了,這個是應(yīng)用首頁,所有可能出現(xiàn)的地方比較多,隨便找一個看看
webView.loadUrl(xx)
這是一個webview 頁面,這個加載了資源為什么也沒有釋放呢?
查找原因 [webview oom](https://my.oschina.net/zhibuji/blog/100580)
Problem 3: 62個ImageView實例 占用的內(nèi)存太大.為什么ImageView 沒有銷毀呢? 并且還這么大呢? 通過跟蹤發(fā)現(xiàn),ImageView有些屬于
啟動之后就依賴的,所以導(dǎo)致他不會消失,特別是如果ImageView 每次都加載圖片, 這個時候就(有可能)導(dǎo)致內(nèi)存占用比較大,并且不銷毀的情況.
內(nèi)存解決方案
程序本身存在內(nèi)存溢出的風(fēng)險,那么,如何才能管控呢?
Bitmap一定要特別注意.如果使用之后,可以調(diào)用recycle 進行回收.bitmap = null 使其變成虛引用,加快GC回收
關(guān)于contenxt 的引用,盡量避免你使用.使用applicationContext 代替,比如 toast ,View 中都要注意避免使用contenxt,
最好全局中static 一個applicationcontext對象方便非Activity使用.
盡量避免使用非靜態(tài)的匿名內(nèi)部類.如果是線程則最好維護一個整個應(yīng)用的線程池.
開發(fā)建議
開發(fā)人員可以通過 leakcanary 來運行debug測試內(nèi)存溢出問題.