本文整理了市面上現(xiàn)有的所有內(nèi)存泄露文章黔衡,加上個(gè)人理解進(jìn)行歸納總結(jié)
描述
內(nèi)存泄露簡(jiǎn)單說(shuō)就是已經(jīng)沒(méi)有用的資源秕磷,但是由于被其他資源引用著
無(wú)法被GC銷毀纯露。
危害
內(nèi)存泄露是內(nèi)存溢出OOM的重要原因之一乖菱,會(huì)導(dǎo)致Crash
如果應(yīng)用程序在消耗光了所有的可用堆空間(16M到48M)静陈,那么再試圖在堆上分配新對(duì)象時(shí)就會(huì)引起OOM(Out Of Memory Error)異常,此時(shí)應(yīng)用程序就會(huì)崩潰退出螟碎。
現(xiàn)在的手機(jī)內(nèi)存越來(lái)越大眉菱,小的內(nèi)存泄漏并不會(huì)有太大危害,但是我們是有夢(mèng)想的程序員掉分,我們想要做出精致的APP俭缓。
檢測(cè)工具
Leaks
https://github.com/square/leakcanary
傻瓜式的內(nèi)存檢測(cè)工具,但是非常好用
當(dāng)然我們可以用AS Monitor+MAT來(lái)自己分析內(nèi)存泄漏原因
http://www.2cto.com/kf/201512/455421.html
從根本上解釋內(nèi)存泄露原因
前文已經(jīng)提到了GC Roots酥郭,內(nèi)存泄露就是因?yàn)閮?nèi)存泄露簡(jiǎn)單說(shuō)就是已經(jīng)沒(méi)有用的資源华坦,但是由于最終被GC Roots引用著無(wú)法被GC銷毀。
Google官方的兩張圖不从,方便理解惜姐,最終藍(lán)色的資源就會(huì)被回收
那么都有哪些資源是GC Roots呢?
1.Class 由System Class Loader/Boot Class Loader加載的類椿息,這些類不會(huì)被回收歹袁。注意是類不會(huì)被回收,實(shí)例還是會(huì)被回收的寝优,但是不依賴實(shí)例的靜態(tài)static變量是依賴類的条舔,因此很多內(nèi)存泄露都是因?yàn)楸混o態(tài)變量引用導(dǎo)致的。
2.Thread 線程乏矾,激活狀態(tài)的線程逞刷;
3.Stack Local 棧中的對(duì)象。每個(gè)線程都會(huì)分配一個(gè)棧妻熊,棧中的局部變量或者參數(shù)都是GC root夸浅,因?yàn)樗鼈兊囊秒S時(shí)可能被用到;
4.JNI Local JNI中的局部變量和參數(shù)引用的對(duì)象扔役;可能在JNI中定義的帆喇,也可能在虛擬機(jī)中定義
5.JNI Global JNI中的全局變量引用的對(duì)象;同上
6.Monitor Used 用于保證同步的對(duì)象亿胸,例如wait()坯钦,notify()中使用的對(duì)象、鎖等侈玄。
7.Held by JVM JVM持有的對(duì)象婉刀。JVM為了特殊用途保留的對(duì)象,它與JVM的具體實(shí)現(xiàn)有關(guān)序仙。比如有System Class Loader, 一些Exceptions對(duì)象突颊,和一些其它的Class Loader。對(duì)于這些類,JVM也沒(méi)有過(guò)多的信息律秃。
也就是說(shuō)所有的內(nèi)存泄漏問(wèn)題從根本上都是因?yàn)楸贿@些GC Root引用著導(dǎo)致的
常見問(wèn)題
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等觀察者注冊(cè)未注銷
處理原則
1.內(nèi)部類靜態(tài)化柜裸,內(nèi)部類里面的資源及時(shí)關(guān)閉不要靜態(tài)化
2.注意線程的及時(shí)關(guān)閉
3.注意資源的及時(shí)關(guān)閉
4.webView單獨(dú)開線程(下面有具體的例子)
5.同樣需要及時(shí)關(guān)閉
自己遇到的內(nèi)存泄露問(wèn)題
一. 單例或一些靜態(tài)資源導(dǎo)致內(nèi)存泄漏(影響較小)
1.InputMethodManager
主要是Android OS遺留的問(wèn)題
google官方確認(rèn)問(wèn)題
https://code.google.com/p/android/issues/detail?id=171190
因?yàn)镮nputMethodManager是單例粱锐,即使泄露也不會(huì)有有很大影響粘室,建議忽略
當(dāng)然也有解決方案
leaks上給的鏈接
https://gist.github.com/pyricau/4df64341cc978a7de414
親測(cè)6.0沒(méi)生效
http://www.reibang.com/p/aa2555628b17親測(cè)生效
2.單例Dialog(或則單例View)
一直保有Context引用,銷毀不了
解決方法就是不用單例了卜范,讓各個(gè)Activity new就可以了
二. 注冊(cè)監(jiān)聽廣播等沒(méi)有注銷(影響較大)
1.RXBUS EventBus等注冊(cè)監(jiān)聽之后沒(méi)有注銷,導(dǎo)致內(nèi)存泄漏
一直保有Context引用鹿榜,銷毀不了
解決方案就是頁(yè)面銷毀是注銷監(jiān)聽
三.WebView內(nèi)存泄露(影響較大)
解決方案是用新的進(jìn)程起含有WebView的Activity
并且在該Activity 的onDestory() 最后加上 System.exit(0); 殺死當(dāng)前進(jìn)程海雪。
微信也是這么做的
下面是一些webView常見問(wèn)題總結(jié)
http://www.cnblogs.com/olartan/p/5713013.html
四.匿名內(nèi)部類(影響較小)
我們這個(gè)問(wèn)題是沒(méi)有應(yīng)用到contex但是卻引用到了
暫時(shí)沒(méi)有好的解決方案舱殿,主要是內(nèi)部類需要提供一些注銷等方法
怎樣避免內(nèi)存泄露
1.使用靜態(tài)變量的時(shí)候要小心奥裸,尤其要注意Activity/Service等大對(duì)象的傳值。在單例模式中能用ApplicationContext的都用ApplicationContext沪袭,或者把聚合關(guān)系改成依賴關(guān)系湾宙,不在單例對(duì)象中持有Context引用;
2.養(yǎng)成良好的代碼習(xí)慣冈绊。注冊(cè)/反注冊(cè)要成對(duì)出現(xiàn)侠鳄,Activity和Service對(duì)象中避免使用非靜態(tài)內(nèi)部類/匿名內(nèi)部類,除非十分清楚引用關(guān)系死宣;
3.使用多線程的時(shí)候留意線程存活時(shí)間伟恶。盡量將聚合關(guān)系改成依賴關(guān)系,減少線程對(duì)象持有大對(duì)象的時(shí)間毅该;
4.在使用xxxStream,SqlLiteDatabase,Cursor類的時(shí)候要注意釋放資源博秫。使用Timer,TimerTask的時(shí)候要記得取消任務(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.