在使用Android Studio進行內存泄露分析以前,咱們先回顧一下Java相關的內存管理機制,而後再講述一下內存分析工具如何使用。java
Java 程序運行時的內存分配策略有三種:靜態分配、棧式分配和堆式分配。程序員
對應的存儲區域以下:數組
棧內存:在方法體內定義的局部變量(一些基本類型的變量和對象的引用變量)都是在方法的棧內存中分配的。當在一段方法塊中定義一個變量時,Java 就會在棧中爲該變量分配內存空間,當超過該變量的做用域後,分配給它的內存空間也將被釋放掉,該內存空間能夠被從新使用。異步
堆內存:用來存放全部由 new 建立的對象(包括該對象其中的全部成員變量)和數組。在堆中分配的內存,將由 Java 垃圾回收器來自動管理。在堆中產生了一個數組或者對象後,還能夠在棧中定義一個特殊的變量,這個變量的取值等於數組或者對象在堆內存中的首地址,這個特殊的變量就是咱們上面說的引用變量。咱們能夠經過這個引用變量來訪問堆中的對象或者數組。ide
Java的內存管理就是對象的分配和釋放問題。內存的分配是由程序員來完成,內存的釋放由GC(垃圾回收機制)完成。GC 爲了可以正確釋放對象,必須監控每個對象的運行狀態,包括對象的申請、引用、被引用、賦值等。這是Java程序運行較慢的緣由之一。工具
該對象再也不被引用。oop
若是一個對象知足如下兩個條件:post
(1)這些對象是可達的,即在有向圖中,存在通路能夠與其相連測試
(2)這些對象是無用的,即程序之後不會再使用這些對象spa
就能夠斷定爲Java中的內存泄漏,這些對象不會被GC所回收,繼續佔用着內存。
在C++中,內存泄漏的範圍更大一些。有些對象被分配了內存空間,而後卻不可達,因爲C++中沒有GC,這些內存將永遠收不回來。在Java中,這些不可達的對象都由GC負責回收。
在Android開發中,常見的單例問題形成內存泄漏的場景以下:
當建立這個單例的時候,因爲須要傳入一個Context,因此這個Context的生命週期的長短相當重要:
1.若是此時傳入的是 Application 的 Context,由於 Application 的生命週期就是整個應用的生命週期,因此沒有任何問題。
2.若是此時傳入的是 Activity 的 Context,當這個 Context 所對應的 Activity 退出時,因爲該 Context 的引用被單例對象所持有,其生命週期等於整個應用程序的生命週期,因此當前 Activity 退出時它的內存並不會被回收,這就形成泄漏了。
固然,Application 的 context 不是萬能的,因此也不能隨便亂用,例如Dialog必須使用 Activity 的 Context。
非靜態內部類默認會持有外部類的引用,而該非靜態內部類又建立了一個靜態的實例,該實例的生命週期和應用的同樣長,這就致使了該靜態實例一直會持有該Activity的引用,致使Activity的內存資源不能正常回收。
匿名內部類默認也會持有外部類的引用。
若是在Activity/Fragment中使用了匿名類,並被異步線程持有,若是沒有任何措施這樣必定會致使泄漏。
例子:Handler形成的內存泄漏。
對於使用了BraodcastReceiver,ContentObserver,File, Cursor,Stream,Bitmap等資源的使用,應該在Activity銷燬時及時關閉或者註銷,不然這些資源將不會被回收,形成內存泄漏。
有些代碼並不形成內存泄漏,可是它們,或是對沒使用的內存沒進行有效及時的釋放,或是沒有有效的利用已有的對象而是頻繁的申請新內存。好比,Adapter裏沒有複用convertView等。
最原始的內存泄漏排查方式以下:
重複屢次操做關鍵的可疑的路徑,從內存監控工具中觀察內存曲線,看是否存在不斷上升的趨勢,且退出一個界面後,程序內存遲遲不下降的話,可能就發生了嚴重的內存泄漏。
這種方式能夠發現最基本,也是最明顯的內存泄漏問題,對用戶價值最大,操做難度小,性價比極高。
下面就開始用一個簡單的例子來講明一下如何排查內存泄漏。
首先,建立了一個TestActivity類,裏面的測試代碼以下:
@Override protected voidprocessBiz() { mHandler = new Handler(); mHandler.postDelayed(newRunnable() { @Override public voidrun() { MLog.d("------postDelayed------"); } }, 800000L); }
運行項目,並執行如下操做:進入TestActivity,而後退出,再從新進入,如此操做幾回後,最後最終退出TestActivity。這時發現,內存持續增高,如圖所示:
這時咱們能夠假設,這裏可能出現了內存泄漏的狀況。那麼,如何繼續定位到內存泄漏的地址呢?這時候就得點擊「Dump java heap」按鈕來收集具體的信息了。
下面咱們就要須要使用Android Studio生成Java Heap文件來分析內存狀況了。
注意,在點擊 Dump java heap 按鈕以前,必定要先點擊Initate GC按鈕強制GC,建議點擊後等待幾秒後再次點擊,嘗試屢次,讓GC更加充分。而後再點擊Dump Java Heap按鈕。
這時候會生成一個Java heap文件並在新的窗口打開:
這時候,點擊右上角的「Analyzer Task」,再點擊出現的綠色按鈕,讓Android studio幫咱們自動分析出有可能潛在的內存泄漏的地方:
如上圖所示,Android studio提示有3個TestActivity對象可能出現了內存泄漏。並且左邊的Reference Tree(引用樹),也大概列出了該實體類被引用的路徑。經過這些咱們就能大概能猜到是哪裏致使了內存泄漏。