Android內存泄露 一篇文章就夠了

本文整理了市面上現有的所有內存泄露文章,加上個人理解進行歸納總結

描述

內存泄露簡單說就是已經沒有用的資源,但是由于被其他資源引用著

無法被GC銷毀。

危害

內存泄露是內存溢出OOM的重要原因之一,會導致Crash

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

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

檢測工具

Leaks

https://github.com/square/leakcanary

傻瓜式的內存檢測工具,但是非常好用

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

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


從根本上解釋內存泄露原因

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

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


Gc 檢測流程1


Gc 檢測流程2

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

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

2.Thread 線程,激活狀態的線程;

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

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

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

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

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

也就是說所有的內存泄漏問題從根本上都是因為被這些GC Root引用著導致的


常見問題

1.非靜態內部類,匿名內部類

2.Thread

3.ContentObserver,File,Cursor,Stream,Bitmap等資源未關閉

4.Webview

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

處理原則

1.內部類靜態化,內部類里面的資源及時關閉不要靜態化

2.注意線程的及時關閉

3.注意資源的及時關閉

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

5.同樣需要及時關閉

自己遇到的內存泄露問題

一. 單例或一些靜態資源導致內存泄漏(影響較小)

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://www.lxweimin.com/p/aa2555628b17親測生效

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

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

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

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

1.RXBUS EventBus等注冊監聽之后沒有注銷,導致內存泄漏

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

解決方案就是頁面銷毀是注銷監聽

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

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

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

微信也是這么做的

下面是一些webView常見問題總結

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

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

我們這個問題是沒有應用到contex但是卻引用到了

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


怎樣避免內存泄露

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

2.養成良好的代碼習慣。注冊/反注冊要成對出現,Activity和Service對象中避免使用非靜態內部類/匿名內部類,除非十分清楚引用關系;

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

4.在使用xxxStream,SqlLiteDatabase,Cursor類的時候要注意釋放資源。使用Timer,TimerTask的時候要記得取消任務。Bitmap在使用結束后要記得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.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容