Android內(nèi)存泄漏

內(nèi)存泄露就是指該被GC垃圾回收的,由于有另外一個(gè)對(duì)象仍然在引用它,導(dǎo)致無(wú)法回收,造成內(nèi)存泄露,過(guò)多的內(nèi)存泄露會(huì)導(dǎo)致OOM。

一、原因及優(yōu)化

1 非靜態(tài)內(nèi)部類(lèi)、匿名內(nèi)部類(lèi)

非靜態(tài)內(nèi)部類(lèi)、匿名內(nèi)部類(lèi) 都會(huì)持有外部類(lèi)的一個(gè)引用,如果有一個(gè)靜態(tài)變量引用了非靜態(tài)內(nèi)部類(lèi)或者匿名內(nèi)部類(lèi),導(dǎo)致非靜態(tài)內(nèi)部類(lèi)或者匿名內(nèi)部類(lèi)的生命周期比外部類(lèi)(Activity)長(zhǎng),就會(huì)導(dǎo)致外部類(lèi)在該被回收的時(shí)候,無(wú)法被回收掉,引起內(nèi)存泄露,除非外部類(lèi)被卸載(JVM自帶的類(lèi)加載器所加載的類(lèi),在虛擬機(jī)的生命周期中,始終不會(huì)被卸載,除非使用自定義的類(lèi)加載器)。

優(yōu)化:
  • 將非靜態(tài)內(nèi)部類(lèi)、匿名內(nèi)部類(lèi) 改成靜態(tài)內(nèi)部類(lèi),或者直接抽離成一個(gè)外部類(lèi)。
  • 如果在靜態(tài)內(nèi)部類(lèi)中,需要引用外部類(lèi)對(duì)象,那么可以將這個(gè)引用封裝在一個(gè)WeakReference中。

2 Handler

主線程的Looper對(duì)象不斷從消息隊(duì)列中取出消息,然后再交給Handler處理。如果在Activity中定義Handler對(duì)象,那么Handler肯定持有Activty的引用,而每個(gè)Message對(duì)象是持有Handler的引用的(Message對(duì)象的target屬性持有Handler引用),從而導(dǎo)致Message間接引用到了Activity。如果在Activty destroy之后,消息隊(duì)列中還有Message對(duì)象,Activty是不會(huì)被回收的。當(dāng)然了,如果消息正在準(zhǔn)備(處于延時(shí)入隊(duì)期間)放入到消息隊(duì)列中也是一樣的。

優(yōu)化:
  • 將Handler放入單獨(dú)的類(lèi)或者將Handler放入到靜態(tài)內(nèi)部類(lèi)中(靜態(tài)內(nèi)部類(lèi)不會(huì)持有外部類(lèi)的引用)。如果想要在Handler內(nèi)部去調(diào)用所在的外部類(lèi)Activity,可以在Handler內(nèi)部使用弱引用的方式指向所在Activity,這樣不會(huì)導(dǎo)致內(nèi)存泄漏。
  • 在onDestory時(shí),調(diào)用相應(yīng)的remove方法移除回調(diào)和刪除消息。

弱引用相關(guān)請(qǐng)移步:Android強(qiáng)引用、弱引用、軟引用

3 靜態(tài)的View

有時(shí),當(dāng)一個(gè)Activity經(jīng)常啟動(dòng),但是對(duì)應(yīng)的View讀取非常耗時(shí),可以通過(guò)靜態(tài)View變量來(lái)保持對(duì)該Activity的rootView引用。這樣就可以不用每次啟動(dòng)Activity都去讀取并渲染View了。這確實(shí)是一個(gè)提高Activity啟動(dòng)速度的好方法。
但是要注意,一旦View attach到Window上,就會(huì)持有一個(gè)Context(即Activity)的引用,而該View又是一個(gè)靜態(tài)變量,所以導(dǎo)致Activity不被回收。

優(yōu)化:
  • 在使用靜態(tài)View時(shí),需要確保在資源回收時(shí),將靜態(tài)View detach掉。

4 監(jiān)聽(tīng)器(各種需要注冊(cè)的Listener、Watcher等)

當(dāng)需要使用系統(tǒng)服務(wù)(比如執(zhí)行某些后臺(tái)任務(wù)、為硬件訪問(wèn)提供接口等等系統(tǒng)服務(wù))時(shí),需要把Activity自己注冊(cè)到服務(wù)的監(jiān)聽(tīng)器中,這會(huì)讓服務(wù)持有Activity的引用,如果沒(méi)有在Activity銷(xiāo)毀時(shí)取消注冊(cè),那就會(huì)導(dǎo)致Activity泄漏。
例如:EditText的addTextChangeListener,如果在回調(diào)方法里有耗時(shí)操作,可能會(huì)造成內(nèi)存泄露。

優(yōu)化:
  • 在onDestory時(shí),取消注冊(cè)。
    比如:editText.removeTextChangedListener

5 WebView

在android 5.1及以上版本的代碼中,WebView可能會(huì)存在內(nèi)存泄露。
(原因可以參考這篇文章:https://blog.csdn.net/u013085697/article/details/53259116

優(yōu)化:
  • 在銷(xiāo)毀WebView前一定要onDetachedFromWindow,先將WebView從它的父View中移除再調(diào)用destroy方法。

6 單例造成的內(nèi)存泄漏

單例的靜態(tài)特性使得單例的生命周期和應(yīng)用的生命周期一樣長(zhǎng),這就說(shuō)明了如果一個(gè)對(duì)象已經(jīng)不需要使用了,而單例對(duì)象還持有該對(duì)象的引用,那么這個(gè)對(duì)象將不能被正?;厥?,這就導(dǎo)致了內(nèi)存泄漏。

7 線程死鎖

8 資源對(duì)象沒(méi)有關(guān)閉

當(dāng)打開(kāi)資源時(shí),一般都會(huì)使用緩存。比如讀寫(xiě)文件資源、打開(kāi)數(shù)據(jù)庫(kù)資源、使用Bitmap資源等等。當(dāng)不再使用時(shí),應(yīng)該關(guān)閉它們,使得緩存內(nèi)存區(qū)域及時(shí)回收。雖然有些對(duì)象,如果不去關(guān)閉,它自己在finalize()函數(shù)中會(huì)自行關(guān)閉,但是這要等到GC回收時(shí)才關(guān)閉,這樣會(huì)導(dǎo)致緩存駐留一段時(shí)間。如果頻繁打開(kāi)資源,內(nèi)存泄漏帶來(lái)的影響就比較明顯了。

9 屬性動(dòng)畫(huà)

在使用ValueAnimator或者ObjectAnimator時(shí),如果沒(méi)有及時(shí)做cancel取消動(dòng)畫(huà),就可能造成內(nèi)存泄露。
因?yàn)樵赾ancel方法里,最后調(diào)用了endAnimation(); ,在endAnimation里,有個(gè)AnimationHandler的單例,會(huì)持有屬性動(dòng)畫(huà)對(duì)象的引用。

優(yōu)化:
  • 在onDestory時(shí),調(diào)用動(dòng)畫(huà)的cancel方法

10 其他的系統(tǒng)控件以及自定義View

在 Android Lollipop 之前使用 AlertDialog 可能會(huì)導(dǎo)致內(nèi)存泄漏,參考:https://blog.csdn.net/u012464435/article/details/50774580。

Dialog和DialogFragment在Android5.0以下的內(nèi)存泄漏,參考:https://www.cnblogs.com/endure/p/7664320.html,http://www.mamicode.com/info-detail-1753936.html。

11 TimerTask


三、檢測(cè)工具

1 命令行查看內(nèi)存占用情況

adb shell dumpsys meminfo -a 包名

2 AS自帶的Monitors

展示內(nèi)存使用,及網(wǎng)絡(luò)、CPU、GPU使用情況。

3 LeakCanary

隨應(yīng)用運(yùn)行,實(shí)時(shí)監(jiān)測(cè)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們?cè)陂_(kāi)發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問(wèn)題。內(nèi)存泄漏...
    _痞子閱讀 1,700評(píng)論 0 8
  • 內(nèi)存管理的目的就是讓我們?cè)陂_(kāi)發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問(wèn)題。內(nèi)存泄漏大家都不陌生了,簡(jiǎn)單粗俗的講,...
    宇宙只有巴掌大閱讀 2,492評(píng)論 0 12
  • Android 內(nèi)存泄漏總結(jié) 內(nèi)存管理的目的就是讓我們?cè)陂_(kāi)發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問(wèn)題。內(nèi)存泄漏...
    apkcore閱讀 1,306評(píng)論 2 7
  • 內(nèi)存管理的目的就是讓我們?cè)陂_(kāi)發(fā)中怎么有效的避免我們的應(yīng)用出現(xiàn)內(nèi)存泄漏的問(wèn)題。內(nèi)存泄漏大家都不陌生了,簡(jiǎn)單粗俗的講,...
    DreamFish閱讀 869評(píng)論 0 5
  • 【Android 內(nèi)存泄漏】 引用: ★★★ 【知識(shí)必備】?jī)?nèi)存泄漏全解析,從此拒絕ANR,讓OOM遠(yuǎn)離你的身邊,跟...
    Rtia閱讀 987評(píng)論 0 2

友情鏈接更多精彩內(nèi)容