一、概述
在Android的開發中,經常聽到“內存泄漏”這個詞。“內存泄漏”就是一個對象已經不需要再使用了,但是因為其它的對象持有該對象的引用,導致它的內存不能被回收?!皟却嫘孤钡穆e累,最終會導致OOM的發生,千里之堤,毀于蟻穴。所以在寫代碼的過程中,應該要注意規避會導致“內存泄漏”的代碼寫法,提高軟件的健壯性。
本文將從發現問題、解決問題、總結問題的三個角度出發,循序漸進,徹底解決“內存泄漏”的問題。
二、內存泄漏的檢查工具Heap
工欲善其事必先利其器,要檢測“內存泄漏”的發生,需要借助DDMS中的Heap工具及MAT工具,Heap工具用于大致分析是否存在“內存泄漏”,而MAT工具則用于分析“內存泄漏”發生在哪里。
Heap工具的使用介紹
具體操作
我們先模擬一下內存泄漏,然后通過Heap工具來判斷一下是否存在內存泄漏。
上一段存在內存泄漏的代碼:
public class LeakAty extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.aty_leak); testLeak(); } /** * 測試內存泄漏的代碼 */ private void testLeak() { new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); }
上述的代碼存在內存泄漏,new Runnable(){}是一個非靜態的匿名內部類,所以它會強引用創建它的外圍對象LeakAty,我們來測試一下內存泄漏的過程,開啟手機的方向旋轉功能,不斷地旋轉手機,讓LeakAty不斷地創建新的實例。理論上如果不存在上述泄漏的代碼,之前的Activity會在onDestory之后被回收內存。而一旦存在上述泄漏的代碼,新創建的Ruannale實例會一直處于運行狀態,它不會被回收,而它強引用的LeakAty當然也不會被回收,所以在屏幕不斷旋轉,之前創建的LeakAty就不會被釋放,會導致旋轉n次,內存中就存在n+1個的LeakAty實例。
Heap工具第一次按下Cause GC按鈕的截圖:
上圖的data object的Total Size的大小為1.031M。經過多次的旋轉屏幕之后,我們再看一下截圖
Total Size變成了2.059M,從1.031M到2.059M,每次調用GC的過程中data object的總大小沒有回落,所以可以證實上面的代碼確實是存在內存泄漏的問題,那么泄漏發生在哪里?答案可以通過MAT工具來分析得到。
三、內存泄漏的分析工具MAT
要通過MAT分析,需要提供一個.hprof文件。我們可以通過”Dump HPROF file”按鈕轉存當前的堆內存信息。我們將其保存為1.hprof。
導出的1.hprof的格式需要通過../sdk/tools/目錄下的hprof-conv.exe工具進行轉換才能被MAT成功導入,我們將其轉換成out1.hprof
將out1.hprof導入到MAT工具中,File->Open Heap Dump…
點擊左邊的標簽Overview,Actions->Histogram
在Histogram界面中,因為我們想要知道Activity是否泄漏了,所以輸入關鍵詞Activity,然后按下回車鍵。
之后便可以得到Activity的相關的搜索結果,下圖的搜索結果中Activity的實例有7個。點擊選中下圖標紅色框框的地方,右鍵->Merge Shortest Paths to GC Roots->exclude all phantom/weak/soft etc. references。排除虛引用、弱引用、軟引用的實例,剩下的都是強引用實例。
從過濾出來的強引用的列表中,我們可以看到這七個實例都是被Thread所引用了。所以證實上面的代碼確實存在內存泄漏。
四、本文總結
內存泄漏檢測可以使用Heap工具,內存分析可以使用MAT工具。本文的案例中提到了一種內存泄漏的情況,就是非靜態內部類的對象會強引用其外圍對象,一旦這個非靜態內部類的實例沒有釋放,它的外圍對象也不會釋放,所以就會造成內存泄漏。下篇將具體探討一下,在Android的開發過程中,哪些寫法容易造成內存泄漏,該如何解決?請閱讀Android內存泄漏終極解決篇(下)。
以上就是本文的全部內容,希望大家喜歡。
新聞熱點
疑難解答
圖片精選