0x00 內(nèi)存泄漏
測試安卓新版本的過程中咒吐,發(fā)現(xiàn)在操作若干次書籍正文之后,雖然沒有出現(xiàn)crash属划,但是會出現(xiàn)頁面卡頓恬叹、時不時會有黑屏、ANR無響應(yīng)等異常同眯,十分影響體驗(yàn)绽昼。于是,我將產(chǎn)生異常時進(jìn)行了日志打印嗽测,看到如下的問題:
此時系統(tǒng)進(jìn)行頻繁的GC操作绪励,懷疑是發(fā)生內(nèi)存泄漏了肿孵。
此時用adb打印下app的內(nèi)存信息唠粥,看下是否有異常:
命令為:adb shell dumpsys meminfo YourPackageName
我們看到View和Activity的數(shù)量非常高,此時再將老版本的內(nèi)存信息打印下:
果不其然停做,新版本的內(nèi)存信息與老版本比較起來晤愧,內(nèi)存中有較多的View和Activity,一定是發(fā)生了內(nèi)存泄漏蛉腌。
0x01 LeakCanary定位
將LeakCanary集成到項(xiàng)目源代碼中官份,集成方法可參考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0511/2861.html
安裝集成LeakCanary的項(xiàng)目app,進(jìn)行使用烙丛,一段時間后舅巷,LeakCanary捕獲到了內(nèi)存泄漏:
然后將log日志拿到:
這里只是截取了一部分,后面還有很長的日志信息河咽,打印出此時的調(diào)用鏈對象中的各個的變量和對應(yīng)的值钠右。
單純的看LeakCanary分析的結(jié)果,確認(rèn)了是此次新增加的NewActivity引入的內(nèi)存泄漏問題忘蟹,也看到com.iflytek.cloud.thirdparty.ae(已經(jīng)過混淆飒房,屬混淆后的類名稱)類對象保持了對該Acticity的引用沒有釋放引發(fā)的問題搁凸。但是還是比較難定位問題,無法直接進(jìn)行解決狠毯。所以使用MAT工具進(jìn)行進(jìn)一步分析护糖。
0x02 MAT分析
使用MAT分析的幾個關(guān)鍵步驟是
- Android Device Monitor拿到Hprof堆文件
- hprof-conv工具轉(zhuǎn)換成MAT可分析的文件
- 使用MAT工具分析
第一步,Android Device Monitor拿到Hprof堆文件
在Android Studio中可以直接打開Android Device Monitor嚼松,不過在AS 3.0版本以上就廢棄Android Device Monitor的工具入口了嫡良,需要在本機(jī)android sdk路徑下打開tools目錄下的Monitor。
如下圖所示惜颇,在Devices列表中選擇我們的測試設(shè)備皆刺,再選擇其中我們項(xiàng)目的包名,選中后點(diǎn)擊更新堆信息按鈕凌摄,繼而操作我們的項(xiàng)目app羡蛾,一段時間發(fā)生內(nèi)存泄漏后,點(diǎn)擊導(dǎo)出HPROF文件按鈕將堆信息文件進(jìn)行導(dǎo)出锨亏,文件是prof格式痴怨。
第二步,hprof-conv工具轉(zhuǎn)換成MAT可分析的文件
hprof-conv工具在系統(tǒng)sdk/platform-tools目錄下器予,使用方法
hprof-conv 輸入文件 輸出文件
得到MAT可分析的hprof文件后浪藻,就可以使用MAT分析啦。
第三步乾翔,使用MAT工具分析
下載MAT工具爱葵,下載地址:https://www.eclipse.org/mat/
打開MAT
點(diǎn)擊File->Open Heap Dump打開上一步轉(zhuǎn)換好的hprof文件:
可以看到內(nèi)存中的所有對象,如下圖所示:
考慮內(nèi)存泄漏較多發(fā)生在Activity級別反浓,所以我們篩選出內(nèi)存的Activity數(shù)據(jù)萌丈。點(diǎn)擊圖中的QQL圖標(biāo) 輸入指令
select * from instanceof android.app.Activity
類似于SQL語句,點(diǎn)擊紅色嘆號執(zhí)行后如下圖所示:
此時展示出了現(xiàn)在內(nèi)存中存留的所有Activity雷则,操作時我們已經(jīng)關(guān)閉了所有xxNewActivity辆雾,但是內(nèi)存中還是有多個xxNewActivity的實(shí)例,證明xxNewActivity發(fā)生了內(nèi)存泄漏月劈。接下來我們要針對這些泄漏的Activity深入分析是哪些實(shí)例沒有釋放導(dǎo)致的泄漏度迂。
在泄漏的Activity上右鍵->Path To GC Roots->with all references:
下圖即為path to gc roots的主要對象:
其實(shí)從GC上說,除了強(qiáng)引用外猜揪,其他的引用在JVM需要的情況下是都可以 被GC掉的惭墓,如果一個對象始終無法被GC,就是因?yàn)閺?qiáng)引用的存在而姐,從而導(dǎo)致在GC的過程中一直得不到回收腊凶,因此就內(nèi)存溢出了。我們可以選擇path to GC roots->exclude all phantom/weak/soft etc.reference:
也就是強(qiáng)引用的對象主要是ShareListsMenu對context的引用,和xxNewActivity內(nèi)部對象的強(qiáng)引用吭狡。
0x03 問題解決
查到問題后尖殃,剩下的就是解決問題了。我們在ShareListsMenu類中的clearData方法中新增對mContexts變量的置空操作划煮,解除對context的引用送丰。
然后我們將修改后的代碼再跑起來,這時候不會發(fā)生內(nèi)存泄漏了弛秋。問題得到了解決器躏。
ps:關(guān)于MAT在mac上的安裝,會出現(xiàn)的問題及解決方法:
https://juejin.im/post/5a81482a6fb9a063331522a6
參考文章:
https://www.cnblogs.com/ldq2016/p/6632338.html
http://www.jb51.net/article/77899.htm
http://wensong.iteye.com/blog/1986449