
本文整理了市面上現(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 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.