Android內(nèi)存泄露 一篇文章就夠了

本文整理了市面上現(xiàn)有的所有內(nèi)存泄露文章,加上個人理解進行歸納總結(jié)

描述

內(nèi)存泄露簡單說就是已經(jīng)沒有用的資源,但是由于被其他資源引用著

無法被GC銷毀。

危害

內(nèi)存泄露是內(nèi)存溢出OOM的重要原因之一,會導(dǎo)致Crash

如果應(yīng)用程序在消耗光了所有的可用堆空間(16M到48M),那么再試圖在堆上分配新對象時就會引起OOM(Out Of Memory Error)異常,此時應(yīng)用程序就會崩潰退出。

現(xiàn)在的手機內(nèi)存越來越大,小的內(nèi)存泄漏并不會有太大危害,但是我們是有夢想的程序員,我們想要做出精致的APP。

檢測工具

Leaks

https://github.com/square/leakcanary

傻瓜式的內(nèi)存檢測工具,但是非常好用

當然我們可以用AS Monitor+MAT來自己分析內(nèi)存泄漏原因

http://www.2cto.com/kf/201512/455421.html


從根本上解釋內(nèi)存泄露原因

前文已經(jīng)提到了GC Roots,內(nèi)存泄露就是因為內(nèi)存泄露簡單說就是已經(jīng)沒有用的資源,但是由于最終被GC Roots引用著無法被GC銷毀。

Google官方的兩張圖,方便理解,最終藍色的資源就會被回收


Gc 檢測流程1


Gc 檢測流程2

那么都有哪些資源是GC Roots呢?

1.Class 由System Class Loader/Boot Class Loader加載的類,這些類不會被回收。注意是類不會被回收,實例還是會被回收的,但是不依賴實例的靜態(tài)static變量是依賴類的,因此很多內(nèi)存泄露都是因為被靜態(tài)變量引用導(dǎo)致的。

2.Thread 線程,激活狀態(tài)的線程;

3.Stack Local 棧中的對象。每個線程都會分配一個棧,棧中的局部變量或者參數(shù)都是GC root,因為它們的引用隨時可能被用到;

4.JNI Local JNI中的局部變量和參數(shù)引用的對象;可能在JNI中定義的,也可能在虛擬機中定義

5.JNI Global JNI中的全局變量引用的對象;同上

6.Monitor Used 用于保證同步的對象,例如wait(),notify()中使用的對象、鎖等。

7.Held by JVM JVM持有的對象。JVM為了特殊用途保留的對象,它與JVM的具體實現(xiàn)有關(guān)。比如有System Class Loader, 一些Exceptions對象,和一些其它的Class Loader。對于這些類,JVM也沒有過多的信息。

也就是說所有的內(nèi)存泄漏問題從根本上都是因為被這些GC Root引用著導(dǎo)致的


常見問題

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

2.Thread

3.ContentObserver,F(xiàn)ile,Cursor,Stream,Bitmap等資源未關(guān)閉

4.Webview

5.BraodcastReceiver,EventBus等觀察者注冊未注銷

處理原則

1.內(nèi)部類靜態(tài)化,內(nèi)部類里面的資源及時關(guān)閉不要靜態(tài)化

2.注意線程的及時關(guān)閉

3.注意資源的及時關(guān)閉

4.webView單獨開線程(下面有具體的例子)

5.同樣需要及時關(guān)閉

自己遇到的內(nèi)存泄露問題

一. 單例或一些靜態(tài)資源導(dǎo)致內(nèi)存泄漏(影響較小)

1.InputMethodManager

主要是Android OS遺留的問題

google官方確認問題

https://code.google.com/p/android/issues/detail?id=171190

因為InputMethodManager是單例,即使泄露也不會有有很大影響,建議忽略

當然也有解決方案

leaks上給的鏈接

https://gist.github.com/pyricau/4df64341cc978a7de414

親測6.0沒生效

http://m.itdecent.cn/p/aa2555628b17親測生效

2.單例Dialog(或則單例View)

一直保有Context引用,銷毀不了

解決方法就是不用單例了,讓各個Activity new就可以了

二. 注冊監(jiān)聽廣播等沒有注銷(影響較大)

1.RXBUS EventBus等注冊監(jiān)聽之后沒有注銷,導(dǎo)致內(nèi)存泄漏

一直保有Context引用,銷毀不了

解決方案就是頁面銷毀是注銷監(jiān)聽

三.WebView內(nèi)存泄露(影響較大)

解決方案是用新的進程起含有WebView的Activity

并且在該Activity 的onDestory() 最后加上 System.exit(0); 殺死當前進程。

微信也是這么做的

下面是一些webView常見問題總結(jié)

http://www.cnblogs.com/olartan/p/5713013.html

四.匿名內(nèi)部類(影響較小)

我們這個問題是沒有應(yīng)用到contex但是卻引用到了

暫時沒有好的解決方案,主要是內(nèi)部類需要提供一些注銷等方法


怎樣避免內(nèi)存泄露

1.使用靜態(tài)變量的時候要小心,尤其要注意Activity/Service等大對象的傳值。在單例模式中能用ApplicationContext的都用ApplicationContext,或者把聚合關(guān)系改成依賴關(guān)系,不在單例對象中持有Context引用;

2.養(yǎng)成良好的代碼習(xí)慣。注冊/反注冊要成對出現(xiàn),Activity和Service對象中避免使用非靜態(tài)內(nèi)部類/匿名內(nèi)部類,除非十分清楚引用關(guān)系;

3.使用多線程的時候留意線程存活時間。盡量將聚合關(guān)系改成依賴關(guān)系,減少線程對象持有大對象的時間;

4.在使用xxxStream,SqlLiteDatabase,Cursor類的時候要注意釋放資源。使用Timer,TimerTask的時候要記得取消任務(wù)。Bitmap在使用結(jié)束后要記得recycler()。

官方推薦:

In summary, to avoid context-related memory leaks, remember the following:

1.Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)

2.Try using the context-application instead of a context-activity

3.Avoid non-static inner classes in an activity if you don’t control their life cycle, use a static inner class and make a weak reference to the activity inside

And remember that a garbage collector is not an insurance against memory leaks. Last but not least, we try to make such leaks harder to make happen whenever we can.

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

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

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