1. 程式人生 > >android記憶體優化之三記憶體分析工具的使用

android記憶體優化之三記憶體分析工具的使用

 anroid記憶體分析工具的使用

一.Eclipse Heap分析記憶體洩露

          Android開發中避免不了碰到記憶體洩露問題,這裡先大概講下記憶體洩露的基本概念:記憶體洩露官方的解釋是是用動態儲存分配函式動態開闢的空間,在使用完畢後未釋放,結果導致一直佔據該記憶體單元,直到程式結束。它也可以理解為new的新物件用完後,該物件沒有得到回收,造成的無用的物件一直佔據著記憶體,這種無用的隨著操作的次數越多,佔據的記憶體越多,直到記憶體溢位程式,報錯停止執行。記憶體溢位問題比起程式直接報錯的問題更難定位,光靠閱讀程式碼來分析記憶體溢位問題工作量也有些大,所以我們就不得不借助工具分析記憶體溢位問題。這一章節介紹的主要是如何使用記憶體分析工具MAT。

我們用的最多的一般都是Eclipse自帶的DDMS工具,進入DDMS後其介面如下:

       這裡先講下該工具的使用步驟:

 1. 通過USB線連線手機到PC上,android除錯終端開啟需要進行記憶體分析的APP,點選DDMS選項,進入堆顯示介面。

 2. 在Devices介面選擇APP的程序名,如圖所示我的APP程序名是com.example.oomtest。

 3. 接著點選如上圖所示的左上角綠色按鈕UpdateHeap,用於更新顯示APP的記憶體堆詳情,點選後出現上圖右邊的Heap介面,即APP的堆的使用情況。其中Heap Size即APP的堆大小,是可變,至於上限是怎麼設定的可以看下上一章節。Allocated即當前APP分配出去的堆大小,一般進行某個操作後是否記憶體洩露可以通過檢視Allocated是否增大進行簡單的判定,但是不一定準確,但是對於明顯的記憶體洩露還是可以的,比如載入大圖片並顯示時。

 4. 上圖右邊的Heap介面,有個Cause GC按鈕是用於觸發你的手機的GC執行緒進行記憶體回收。這裡要提下的是一些人在監聽某個介面的堆情況時,進入某個介面退出後根本沒有點選Cause GC,此時發現退出介面後Allocated大小增大了,以為就是記憶體洩露了。其實不是的,只是有時GC執行緒自己還沒掃描到你的APP,此時該APP的堆是還沒有被回收的,所以Allocated的大小比之前的增加了。所以在進行某個操作後,要檢視APP的堆情況還要點選Cause GC,建議多點選幾次。

 5 . 步驟5的Dump HPROF file按鈕是用於下載heap記憶體分析的檔案,接著要講的分析工具講用到該檔案。

       看到這裡可能有些網友會問了,有些程式碼明明是記憶體洩露了,為什麼Allocted還是顯示正常的,即操作前和操作後,Allocted大小都沒變化。是的,這裡也是使用DDMS檢視記憶體洩露的缺點,一些隱蔽性比較強的記憶體洩露,用DDMS是看不出來的。所以第二節要講的是如何通過Memory Analyer工具進行記憶體洩露分析,該方法會準確很多,也方便問題的定位。

二. Memory Analyer Tools的Program supect分析記憶體洩露

       這裡要介紹的工具不是Eclipse自帶的,所以需要下載Memory Analyer Tools(簡稱MAT,也可以作為外掛整合到Eclipse中),本人是自己下載Memory Analyer工具。安裝好Memory Analyer工具後,接著講操作步驟:

1.  點選上圖中的步驟5中的Dump HPROF file按鈕下載堆分析檔案,即字尾名為hprof的檔案。

2.  由於下載的hprof檔案在MAT中無法正常讀入,所以需要sdk中的hprof-conv進行轉換才可以在MAT正常讀入。為了方便檔案轉換,接著進入sdk中檢視hprof-conv工具放在什麼位置,建議未轉換前的hprof檔案跟hprof-conv放在同一目錄下,因為接著在cmd中進行轉換時不用輸入太長的檔案路徑。本人這裡的hprof-conv工具的絕對路徑為D:\ProgramFiles\eclipse\Android-sdk-windows-full\platform-tools>hprof-conv,同時也把轉換前的hprof檔案也放到這個位置。

3.  開啟cmd,進入到hprof-conv所在的目錄下,輸入hprof-conv  old.hprof  new.hprof後按回車鍵,接著在D:\ProgramFiles\eclipse\Android-sdk-windows-full\platform-tools目錄下生成的new.hprof檔案,即為MAT可以讀入的hprof檔案。注:其中old.hprof為轉換前的檔名,new.hprof為轉換後的檔名。

4.  開啟MAT,並匯入轉換後的hprof檔案,操作如下圖右上角所示:

 

5.  匯入後的介面如下圖所示,介面上的Problemsupect就是MAT幫你找出來的可能記憶體洩露的地方,看到這裡你可能會想,這樣很方便啊,MAT都幫你把所有可能記憶體洩露的地方都找出來了,接著點選Problem Supect一個一個慢慢看就可以了,其實網上很多貼也是這麼說的。在這裡我想說通過Problem Supect來定位某些記憶體洩露問題(比如載入大圖片或者載入很多小圖片的情況下)也是一種方法,但是此方法跟Eclipse的Heap來分析記憶體洩露一樣,也是不準確的。

   

三 . 使用MAT準確有效的分析方法

       下面重點介紹下另一種MAT分析記憶體洩露的方法,到這裡你可能又會有疑問了:什麼樣的記憶體洩露是上面介紹的兩個方法可能檢測不出來的?這裡也順便說下吧。

(1). Activity的context被生命週期更長的物件(內部類和靜態變數等)佔據,導致的記憶體洩露問題。

(2). Cursor用完沒有colse造成的記憶體洩露問題。

        這裡先介紹下幾個基本概念,後面會用到: (1)Shallow Heap是物件本身佔據的記憶體的大小,不包含其引用的物件。對於常規物件(非陣列)的Shallow Size由其成員變數的數量和型別來定,而陣列的ShallowSize由陣列型別和陣列長度來決定,它為陣列元素大小的總和。(2)Retained Heap為當前物件大小+當前物件可直接或間接引用到的物件的大小總和。(間接引用的含義:A->B->C,C就是間接引用) ,並且排除被GC Roots直接或者間接引用的物件。

       操作步驟:


(1).步驟1,如上圖所示,點選Overview選項,進入到主介面。

(2).步驟2,如上圖所示,點選Histogram選項,進入到Histogram介面,如下圖所示。其中程序名輸入框用於輸入APP的程序名,從而可以檢視APP的堆情況。

       下面用一個記憶體洩露例子簡單分析下,在Activity中定義一個執行緒內部類,並在執行緒中長時間休眠,該測試APP的程序名為com.example.oomtest,關鍵程式碼如下:

protectedvoid onCreate(BundlesavedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

Log.i(TAG,"onCreate");

setContentView(R.layout.activity_second);

new TestThread().start();

}

publicclass TestThreadextends Thread {

@Override

publicvoid run() {

super.run();

try {

Thread.sleep(1000 * 60 *1000);

}catch (InterruptedExceptione) {

e.printStackTrace();

}

}

}

        進入測試介面並退出該介面操作一次,我們在獲取hropf檔案之後,轉換並匯入MAT中,開啟Histogram介面後,在<Regex>中輸入apk的程序名字,比如本程式使用的是com.example.oomtest,接著介面會列出APP的堆情況,如下圖所示。

 


       從上圖中的Objects列中可以看出當退出測試介面SencondActivity時,它的例項存在。所以可以判定SencondActivity發生了記憶體洩露,但是使用前面介紹的兩種方法根本就分析不出APP發生了記憶體洩露。到這裡你可能也會問,知道了SencondActivity發生了記憶體洩露,那麼怎麼知道具體是哪裡發生了記憶體洩露?這個也簡單,這也是Histogram分析記憶體洩露問題的優勢,操作步驟如下圖所示。

跟著上圖所示操作,接著出現以下介面,從下圖中可以看出activity例項沒有被回收,這就是前面所說的隱藏性比較強的記憶體洩漏型別1了,主要原因就是Activity中的執行緒一直佔住了Activity的this造成的記憶體洩露,所以當該Activity被Destroy時,Activity的例項沒法被GC回收。

         現將執行緒的休眠時間修改為100時,按同樣的操作方法測試,SencondActivity的子執行緒由於休眠時間比較短,此時當Activity被Destroy時,Activity的this已經被釋放了,所以不會發生記憶體洩露的情況,其堆記憶體情況如圖所示。

        從列表中可以看出此時SencondACtity的Object(物件)為0,即SencondActivity沒有存在物件,所以用該方法進行記憶體洩露分析是很準確的,但是同樣的程式通過前面的兩種方法就無法定位出哪裡出問題,甚至檢測不出記憶體洩露了。這一章就先介紹到這裡吧,下一章節將介紹其他一些隱藏性比較強的記憶體洩露問題,以便各大網友開發出高質量的APP