簡單判斷是否有內(nèi)存泄漏
判斷內(nèi)存泄漏的定位的大單位是Activity裆赵。
可以通過反復(fù)進(jìn)入退出一個(gè)Activity,然后用adb shell dumpsys meminfo + 包名 查看虛擬機(jī)的堆是否有不斷地增長
定位內(nèi)存泄漏
1.使用Leak Canary
在代碼上加入Leak Canary跺嗽,然后不斷跑Monkey或者手動(dòng)反復(fù)進(jìn)出不同頁面战授。若出現(xiàn)內(nèi)存泄漏問題,會(huì)自動(dòng)導(dǎo)出來桨嫁,生成以下頁面植兰。
2.使用DDMS導(dǎo)出hprof,并用MAT工具進(jìn)行分析
0)強(qiáng)烈建議先跑30分鐘Monkey測試
1)使用eclipse的ddms找到對(duì)應(yīng)的進(jìn)程璃吧,觸發(fā)一次gc后楣导,dump出里面的內(nèi)存快照hprof文件以分析當(dāng)前應(yīng)用內(nèi)存的堆有什么東西
2)使用Android SDK 里的platform-tools文件夾的 hprof-conv工具,對(duì)剛才 hprof 文件進(jìn)行轉(zhuǎn)換畜挨,以至于 后面MAT工具能正常打開
3)使用MAT打開hprof文件筒繁,進(jìn)入Histogram。輸入自己猜測可能泄漏的Activity(項(xiàng)目中Activity不多時(shí)巴元,可每個(gè)Activity都重復(fù)以下3毡咏、4、5步驟)
4)右鍵該其中一項(xiàng)逮刨,打開菜單選擇list objects ->with incoming refs將列出該類的實(shí)例
下圖展示了對(duì)象間的引用關(guān)系呕缭。
5)右健Path to GC Roots-->exclue all phantom/weak/soft etc. reference,找出這個(gè)實(shí)例GC后禀忆,還會(huì)存在什么對(duì)象的引用關(guān)系臊旭。
下圖的情況是:NetworkChangeNotifier類導(dǎo)致BrowserActivity導(dǎo)致內(nèi)存泄漏
常見導(dǎo)致內(nèi)存泄漏的幾個(gè)點(diǎn)
生命周期的原因
比如:Activity中關(guān)聯(lián)了一個(gè)生命周期超過Activity的Thread,這個(gè)Thread 若持有該Activity的引用箩退,就會(huì)導(dǎo)致內(nèi)存泄漏离熏。
內(nèi)部類的原因
因?yàn)閮?nèi)部類會(huì)隱式地持有外部類的引用,若內(nèi)部類不被釋放戴涝,外部類也是無法釋放滋戳。常見的有內(nèi)部的Listener钻蔑、Callback、Handler等導(dǎo)致奸鸯。
情景1:若外部類應(yīng)該釋放的時(shí)候咪笑,內(nèi)部類還在執(zhí)行里面的函數(shù),會(huì)導(dǎo)致外部類無法釋放娄涩。
情景2:若一個(gè)異步操作窗怒,會(huì)回調(diào)內(nèi)部類的Listener、Callback蓄拣、Handler扬虚。當(dāng)外部類應(yīng)該釋放的時(shí)候,但是這個(gè)異步操作還存在球恤,而這個(gè)異步操作類又持有了Listener辜昵、Callback、Handler咽斧,導(dǎo)致外部類無法被釋放堪置。PS:這個(gè)原因也屬于生命周期的原因。
靜態(tài)變量的原因
單例類里包含Activity
靜態(tài)變量的類里引用到Activity
注冊(cè)與反注冊(cè)张惹、打開與關(guān)閉沒成對(duì)出現(xiàn)的原因
比如:注冊(cè)廣播接收器舀锨、注冊(cè)觀察者(典型的譬如數(shù)據(jù)庫的監(jiān)聽)等⊥鸲海或者自己寫的跟Activity引用有關(guān)的clear()函數(shù)沒有成對(duì)出現(xiàn)
解決方法
1)解決內(nèi)部類的問題(以Handler作為例子)
- onDestroy時(shí)候remove所有msgActivity finish后未處理的msg是問題根源雁竞,所以清空所有未被執(zhí)行的msg
mHandler.removeCallbacksAndMessages(null);
PS:比如Listener、Callback等其他內(nèi)部類的問題拧额,頁面退出的時(shí)候,應(yīng)該完成必要的清理操作彪腔,比如Cancel 請(qǐng)求
- 使用靜態(tài)內(nèi)部類 + weakReference
靜態(tài)內(nèi)部類不會(huì)保留對(duì)外部類的引用侥锦,如果一定要引用外部類,使用weakReference
static class MyHandler extends Handler {
WeakReference<Activity > mActivityReference;
MyHandler(Activity activity) {
mActivityReference= new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
mImageView.setImageBitmap(mBitmap);
}
}
}
PS:比如Listener德挣、Callback等其他內(nèi)部類的問題恭垦,也可以通過這個(gè)方法來解決
2)單例類里面盡量不要傳入Activity,最好穿入ApplicationContext格嗅。假如傳入了Activity番挺,持有的時(shí)長也不能大于Activity的生命周期
3)對(duì)象的注冊(cè)與反注冊(cè)要成對(duì)出現(xiàn)
4)不使用WebView對(duì)象時(shí),應(yīng)該調(diào)用它的destory()函數(shù)來銷毀它屯掖,并釋放其占用的內(nèi)存
5)因?yàn)閂iew會(huì)持有Context玄柏,所以注意不要異步引用View,不要讓靜態(tài)對(duì)象持有View贴铜,不要在集合框架中存儲(chǔ)View