內(nèi)存泄漏原因
本質(zhì):對象的引用未被釋放,導(dǎo)致對象本身無法被有效的回收。(生命周期長的持有生命周期短的引用,導(dǎo)致對象無法被回收)。
內(nèi)存泄漏常見場景
(1)Handler使用不當(dāng)造成的內(nèi)存泄漏
原因:handler作為內(nèi)部類使用,持有外部類的引用。Handler一直持有Activity的引用,其發(fā)送的Message中持有Handler的引用,當(dāng)隊列處理Message的時間過長會導(dǎo)致Handler無法被回收,那么activity也無法被回收。
解決方法:1.將handler聲明為靜態(tài)內(nèi)部類,就不會持有外部類的引用,其生命周期就和外部類無關(guān)。
2.銷毀對象時候清空隊列里的Message。
(2)單例持有activity的引用
原因:單例模式里的靜態(tài)實例持有對象的引用,導(dǎo)致對象無法被回收,常見為持有Activity的引用
解決方法:Context是ApplicationContext,由于ApplicationContext的生命周期是和app一致的,不會導(dǎo)致內(nèi)存泄漏
(3)靜態(tài)集合類
原因:如HashMap、LinkedList等等。如果這些容器為靜態(tài)的,那么它們的生命周期與程序一致,則容器中的對象在程序結(jié)束之前將不能被釋放,從而造成內(nèi)存泄漏。簡單而言,長生命周期的對象持有短生命周期對象的引用,盡管短生命周期的對象不再使用,但是因為長生命周期對象持有它的引用而導(dǎo)致不能被回收。
解決方法:有對應(yīng)的刪除或卸載操作
(4)?線程的操作不當(dāng)引發(fā)的內(nèi)存泄漏
原因:線程產(chǎn)生內(nèi)存泄露的主要原因在于線程生命周期的不可控,activity finsh的時候 線程還在執(zhí)行 導(dǎo)致aciivity不能被銷毀,不能回收。
解決方法:靜態(tài)實例+弱引用(Weakrefrence)方式,使其生命周期一致
(5)常用的資源未關(guān)閉回收引發(fā)的內(nèi)存泄漏
原因:BraodcastReceiver,F(xiàn)ile,Cursor,IO流,Bitmap等資源使用未關(guān)閉
解決方法:使用后有對應(yīng)的關(guān)閉和卸載機(jī)制
(6)匿名內(nèi)部類/非靜態(tài)內(nèi)部類操作不當(dāng)引發(fā)的內(nèi)存泄漏
原因:內(nèi)部類持有對象引用,導(dǎo)致無法釋放,比如各種回調(diào)
解決方法:保持生命周期一致,改為靜態(tài)實例+對象的弱引用方式(WeakReference)
(7)屬性動畫
原因:動畫同樣是一個耗時任務(wù),比如在 Activity 中啟動了屬性動畫(ObjectAnimator),但是在銷毀的時候沒有調(diào)用 cancel 方法,雖然我們看不到動畫了,但是這個動畫依然會不斷地播放下去,動畫引用所在的控件,所在的控件引用 Activity ,這就造成 Activity 無法正常釋放。因此同樣要在 Activity 銷毀的時候 cancel 掉屬性動畫,避免發(fā)生內(nèi)存泄漏。
(8)靜態(tài)變量導(dǎo)致內(nèi)存泄漏
原因:靜態(tài)變量存儲在方法區(qū),它的生命周期從類加載開始,到整個進(jìn)程結(jié)束。一旦靜態(tài)變量初始化后,它所持有的引用只有等到進(jìn)程結(jié)束才會釋放
(9)WebView 造成內(nèi)存泄漏
原因:關(guān)于WebView的內(nèi)存泄漏,因為WebView在加載網(wǎng)頁后會長期占用內(nèi)存而不能被釋放,因此我們在Activity銷毀后要調(diào)用它的destroy()方法來銷毀它以釋放內(nèi)存。 另外在查閱 WebView 內(nèi)存泄漏相關(guān)資料時看到這種情況:WebView下面的Callback持有Activity引用,造成WebView內(nèi)存無法釋放,即使是調(diào)用了WebView.destroy()等方法都無法解決問題(Android 5.1 之后)。 最終的解決方案是:在銷毀 WebView 之前需要先將 WebView 從父容器中移除,然后在銷毀 WebView。
總結(jié)
內(nèi)存泄漏在 Android 內(nèi)存優(yōu)化是一個比較重要的一個方面,很多時候程序中發(fā)生了內(nèi)存泄漏我們不一定就能注意到,所有在編碼的過程中養(yǎng)成良好的習(xí)慣。總結(jié)下來只要做到以下這幾點(diǎn)就能避免大多數(shù)情況的內(nèi)存泄漏:
構(gòu)造單例的時候盡量別用 Activity 的引用
靜態(tài)引用是注意引用對象的置空或者少用靜態(tài)引用
使用靜態(tài)內(nèi)部類 + 弱引用代替非靜態(tài)內(nèi)部類
及時取消廣播或者觀察者注冊
耗時任務(wù)、屬性動畫在 Activity 銷毀時記得 cancel
文件流、Cursor 等資源及時關(guān)閉
Activity 銷毀時 WebView 的移除和銷毀