一、摘要
該部分屬于進(jìn)階內(nèi)容,要先掌握了java內(nèi)存回收機(jī)制,說白了就是引用計數(shù)法和可達(dá)性分析法。但是代碼寫的再認(rèn)真,也難免出現(xiàn)一兩個差錯。這一兩個差錯就會導(dǎo)致內(nèi)存泄漏,輕則內(nèi)存增大,重則內(nèi)存溢出。
二、背景
自從引入了WebView,內(nèi)存變得難以測試,因為WebView內(nèi)存不可控,一加載就導(dǎo)致內(nèi)存暴漲,所以最近比較少跑內(nèi)存測試了。直至在jara系統(tǒng)上反饋了項目出現(xiàn)內(nèi)存crash,沒辦法,必須得處理了。
現(xiàn)象,內(nèi)存達(dá)到了300m。
三、推廣建議
理解原理再深,也無法徹底避免問題的處理。就算了經(jīng)過多次代碼審查,也難以發(fā)現(xiàn)細(xì)節(jié)上的錯誤,還是要通過測試驗證,反復(fù)排查,才能完成避免問題的出現(xiàn)。
大部分人都喜歡聲明成員變量,內(nèi)部類。因為他們的作用域比較廣,代碼寫起來比較爽。但是違反了“最小作用域原則”。往往就會導(dǎo)致內(nèi)存泄漏。
最終,項目經(jīng)過優(yōu)化,內(nèi)存減少到72M
四、正文
問題復(fù)現(xiàn):跑monkey測試,在過程中執(zhí)行:adb shell dumpsys meminfo 包名。就算可以輸出應(yīng)用的內(nèi)存信息。多次執(zhí)行就可以輸出內(nèi)存曲線圖,查看內(nèi)存情況。
排除干擾:在一次競品分析中,發(fā)現(xiàn)小米用戶中心,把WebView都放在一個獨立的進(jìn)程,這樣就可以排除干擾了,也可以使apk輕量化。
重要步驟:
1、 手動排除問題,進(jìn)入一個頁面前,執(zhí)行adb shell dumpsys meminfo 包名。查看內(nèi)存,進(jìn)入后再退出頁面,再次執(zhí)行:adb shell dumpsys meminfo 包名。對比兩次的內(nèi)存情況是否一致。如果出現(xiàn)內(nèi)存異常,重復(fù)操作多次,確認(rèn)那個頁面異常了。
說明:主要關(guān)注一下參數(shù):
? ? Native Head:Native內(nèi)存
? ? DalvikHead:虛擬機(jī)內(nèi)存
? ? EGL/GL:圖像渲染相關(guān)。(activity泄漏時,該項會很大)
? ? Views/AppContexts/Activvities:實例,需要重點關(guān)注這三個參數(shù)
2、進(jìn)一步排查
?? 基本上,經(jīng)過上一步的操作,百分之九十的內(nèi)存問題都會發(fā)現(xiàn)并且解決。還有比較難復(fù)現(xiàn)的情況,需要在monkey情況下復(fù)現(xiàn)。
排查方法:mat
方法:打一個debug版本的apk,跑monkey。跑完后退出頁面,執(zhí)行:
adb shell am dumpheap 包名 /data/local/tmp/test.hprof
adb pull /data/local/tmp/test.hprof
hprof-conv test.hprof temp1.hprof
使用mat打開temp1.hprof
查看那些對象占有大,排查代碼
重要,點擊QQL,執(zhí)行查詢方法。
常用:
select* from instanceof android.graphics.Bitmap
select* from instanceof android.app.Activity?
select* from instanceof android.support.v4.app.Fragment
查詢出那些比較可能泄漏的實例。
因為dump前頁面已經(jīng)退出了,所以存在的實例基本都是泄漏。都要處理。
泄漏點排查。右鍵對象,with all reference。查詢有那些對象長期引用它,導(dǎo)致它無法釋放。
常見案例:
1、 xxxListener:如果你的對象實現(xiàn)了xxxListener方法,或者存在xxxListener的內(nèi)部類,一定要注意反注銷。(onClickListener會自己銷毀的)
2、 所有Context,fragment被作為成員變量,參數(shù)的地方,都要考慮是否會長時間引用。(mvp中會)
3、 廣播的注冊與注銷
4、 Bitmap要及時回收,Bitmap屬于大對象,會直接存在于老年代,最好是不用就回收,減少內(nèi)存
5、EventBus反注銷