內(nèi)存溢出(out of memery)
定義:APP運行時占用的內(nèi)存超出系統(tǒng)分配給該APP的內(nèi)存,就會出現(xiàn)內(nèi)存溢出。
原因:
遞歸(解決方法:增加條件),
死循環(huán)(解決方法:增加循環(huán)時間)
圖片加載(主要原因)超大圖片(利用壓縮工具壓縮圖片,裁剪,Glide,LRU算法)大量加載圖片并沒有及時做回收或銷毀處理
Fragment大量加載,未做好回收、解除綁定的處理(用replace方法)
大量的內(nèi)存泄漏
內(nèi)存泄漏
定義:當(dāng)activity銷毀后,gc在回收該實例的時候,發(fā)現(xiàn)該activity被其他對象持有引用,導(dǎo)致該activity不能被回收,出現(xiàn)內(nèi)存泄漏。
檢測:
1,在Android studio3.0以前,在下邊有一個Android Monitor,在Android studio3.0以后,將其改為了Android profiler,里邊可以分析CPU使用率,內(nèi)存、包括網(wǎng)絡(luò)狀態(tài),一般檢測內(nèi)存泄漏,只需要點擊memory,然后確保移動端啟動的程序和studio工具正確連接,如果要檢測當(dāng)前頁面是否存在泄漏問題,只需要將該activity finish,然后底部有一個小垃圾桶,他是起到手動觸發(fā)gc的功能,通過手動回收,然后再去包下查看該應(yīng)用當(dāng)前存留的實例,如果剛剛finish并且gc的activity仍然存在,證明在這個activity當(dāng)中存在內(nèi)存泄漏。點擊額外生成的activity(一般是后邊帶有$這個符號),一般在studio右邊會有泄漏原因的提示,當(dāng)然具體位置還需要程序員通過經(jīng)驗判斷,主動到項目業(yè)務(wù)中查詢。
2,可以使用leakcanary框架,這個相對比較簡單,只需要添加依賴,在application啟動的時候,判斷是否有l(wèi)eakcanary對該進(jìn)程泄漏的監(jiān)聽,如果沒有,在程序啟動的時候安裝leakcanary即可。
常見案例
1,handler耗時引發(fā)的內(nèi)存泄漏
當(dāng)activity當(dāng)中存在handler接收耗時的消息時,比如我們一般在網(wǎng)絡(luò)請求切換線程時,經(jīng)常使用到handler,假設(shè)消息還沒有發(fā)送完成,但是頁面已經(jīng)被關(guān)閉,也就說activity已經(jīng)執(zhí)行了ondestroy方法。當(dāng)gc回收時,會出現(xiàn)該activity不能被回收的情況,導(dǎo)致內(nèi)存泄漏。
解決辦法:當(dāng)activity銷毀的時候,調(diào)用handler的removeCallbacksAndMessages方法,移除消息任務(wù),然后將handler對象及線程置空。
2,內(nèi)部類引發(fā)的內(nèi)存泄漏(當(dāng)然handler或子線程一般也作為內(nèi)部類使用)
因為java當(dāng)中,內(nèi)部類默認(rèn)持有外部類的引用,當(dāng)外部類銷毀后,一旦gc回收該實例,發(fā)現(xiàn)內(nèi)部類持有他的引用而導(dǎo)致不能回收該實例,出現(xiàn)內(nèi)存泄漏的情況。
解決方法:將內(nèi)部類改為靜態(tài)內(nèi)部類,因為靜態(tài)內(nèi)部類生命周期和應(yīng)用一樣長,所以當(dāng)退出程序的時候會一同回收該實例,并不會影響外部類的回收。
3,單例導(dǎo)致的內(nèi)存泄漏
因為在使用單例的時候,經(jīng)常會傳入一個本類的上下文對象,而單例是靜態(tài)的,生命周期和application一樣長,當(dāng)activity銷毀的時候,該單例持有activity的引用導(dǎo)致其不能被回收,出現(xiàn)內(nèi)存泄漏。
解決方法:在使用上下文的時候,傳全局上下文。
4,資源未關(guān)閉
Cursor,stream,database,Butterknife,broadcastreciver,bindservice,eventBus
比如這些東西在使用完成后,需要進(jìn)行close或者Unbind處理,以節(jié)省內(nèi)存
5,Bitmap對象不在使用時調(diào)用recycle()釋放內(nèi)存
6,Timer計時器、動畫,
因為這些涉及耗時問題,如果activity銷毀,而該任務(wù)并未執(zhí)行完成,會導(dǎo)致內(nèi)存泄漏,所以一般在activity中如果使用到這些耗時任務(wù),需要在activity銷毀時,做對應(yīng)處理,比如調(diào)用timer的cancel方法,或者動畫的cancel方法并將對象置空
7,一些監(jiān)聽器的內(nèi)存泄漏
比如說我們給edittext設(shè)置輸入文字監(jiān)聽時,當(dāng)監(jiān)聽到文字發(fā)生變化,我們通過獲取變化后的文字執(zhí)行了耗時任務(wù)(比如獲取到edittext里的內(nèi)容上傳服務(wù)器),當(dāng)耗時任務(wù)未執(zhí)行完成activity銷毀了,會引發(fā)內(nèi)存泄漏,所以在onDestory時,取消注冊,比如說editText調(diào)用removeTextChangedListener方法
8,Rxjava的內(nèi)存泄漏:
因為rxjava采用的是觀察者模式,當(dāng)請求到數(shù)據(jù)后會根據(jù)訂閱關(guān)系將數(shù)據(jù)發(fā)送到訂閱者,而如果這時訂閱者已經(jīng)銷毀,就會出現(xiàn)引用該對象導(dǎo)致其不能被回收的情況,出現(xiàn)內(nèi)存泄漏,rxjava2發(fā)布的時候也發(fā)現(xiàn)了這個問題,所以在回調(diào)當(dāng)中,新增加了onSubcribe回調(diào),同時返回了一個disposable對象,可以通過判斷disposable里的isDisposed來確定當(dāng)前的訂閱關(guān)系,如果訂閱關(guān)系中的訂閱者已經(jīng)不存在且當(dāng)前訂閱關(guān)系存在,解除訂閱關(guān)系,并終止數(shù)據(jù)的發(fā)送。
9,webView引發(fā)的內(nèi)存泄漏:
因為webview在使用的時候一般持有activity的引用,我們一般在activity的onDestroy方法中調(diào)用mWebView.destroy();來釋放webview。如果在onDetachedFromWindow之前調(diào)用了destroy那就肯定會無法正常反注冊了,也就會導(dǎo)致內(nèi)存泄漏。所以在銷毀webview前一定要先在onDetachedFromWindow中將webview從它的父view中移除,再調(diào)用destroy方法中調(diào)用webview的destroy,我開發(fā)的時候在5.1以上的手機(jī)上發(fā)現(xiàn)這種問題比較多,因為現(xiàn)在5.1以下適配的比較少了,基本沒咋注意。
10,線程導(dǎo)致的內(nèi)存泄漏:
一般使用子線程都會創(chuàng)建一個內(nèi)部類對象,而創(chuàng)建線程一般執(zhí)行耗時任務(wù),所以這個內(nèi)部類默認(rèn)持有外部類的引用,如果耗時任務(wù)在activity銷毀的時候未執(zhí)行完成,會因為持有外部類引用導(dǎo)致外部類不能被回收
11,MVP內(nèi)存泄漏:
MVP實現(xiàn)了view層和model層的徹底分離,P層作為view層和model層的中間層,view層需要通過P層執(zhí)行耗時任務(wù),P層一般持有view的引用,如果m層的耗時任務(wù)還沒有執(zhí)行完成,這時候view層被銷毀了,會出現(xiàn)由于p持有view的引用導(dǎo)致view不能被回收,出現(xiàn)內(nèi)存泄漏的問題