安卓 內存泄漏 MemoryAnalyzer

韓夢飛沙 yue31313 韓亞飛 han_meng_fei_sha 313134555@qq.comhtml

須要 獲取 root 權限java

步驟:android

1,使用eclipse 自帶的 DDMS 工具分析各線程的內存使用狀況,以下圖所示數組

 

Heap視圖界面會定時刷新,在對應用的不斷的操做過程當中就能夠看到內存使用的變化。app

怎樣判斷當前進程是否有內存泄漏呢?dom

這裏須要注意一個值:VM Heap頁面中部有一個data object選項,即數據對象,也就是咱們的程序中大量存在的類類型的對象。eclipse

在data object一行中有一列是「Total Size」,其值就是當前進程中全部Java數據對象的內存總量,通常狀況下,這個值的大小決定了是否會有內存泄漏。如上圖中選中行所示。ide

能夠據此判斷內存有泄漏:
1) 不斷的操做當前應用,或者重複某一動做,注意觀察data object的Total Size值。

工具

2) 正常狀況下Total Size值都會穩定在一個有限的範圍內,也就是說若是程序中的的代碼邏輯良好,優化

沒有建立的對象不被GC機制正常回收的狀況,即使 咱們不斷的操做生成不少對象,而在虛擬機不斷的進行垃圾回收的過程當中,這些對象都被正常回收了,內存使用量會保持在一個比較穩定的水平。

3) 若是代碼中存在對象引用沒有釋放的狀況,則data object的Total Size值在每次GC後不會有明顯的回落,隨着操做次數的增多Total Size的值會愈來愈大。
正常狀況下,一個虛擬機的進程的內存在64M, 若是內存泄漏會發現 Heap Size 在不斷的逼近 64M, 一旦達到這個值時,就會出現退出應用等狀況。


發生內存泄露,Total Size的值愈來愈大時,按下「Dump HPROF file」按鈕,這個時候會提示設置hprof文件的保存路徑。保存後,能夠對比log來分析是哪些操做形成了內存泄漏。

2,點擊 按鈕,導出 hprof 文件,使用MAT 工具進行分析。具體分析步驟和過程詳見下面連接

http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html

3,打開 MAT 工具,File-->Open Heap Dump... 選擇你剛剛保存的 hprof 文件打開

此時,會彈出一個錯誤,以下圖所示:

提示:  Unknown HPROF Version (Java PROFILE 1.0.3) (java.io.IOException)

哦,不要覺得是 MAT 工具版本不對,實際上是 Android 的 hprof 文件在這裏須要進行轉換一下格式纔可使用 MAT 打開,不知道 谷歌在這裏

搗了什麼鬼,難道是優化?

使用 android sdk 目錄下的 tools 中一個工具進行轉化一下

 

4,使用AndrodiSDK/tools/hprof-conv轉化hprof文件, 

首先,要經過控制檯進入到你的 android sdk tools 目錄下

例如 hprof-conv input.hprof     out.hprof

再使用MAT工具打開轉換後的 hprof 文件,就能看到完整的內存使用分析報告了。

以下所示是 MAT 分析內存使用的主界面:

點擊上圖中的 Reports -->Leak Suspects 則能夠進一步看到更詳細的內存泄漏疑點。

在其中懷疑的地方,點擊 Details 就能夠看到具體的內存使用狀況了。

tip1:

有一種比較好的方法是,在內存泄漏開始時抓取一個 hprof 文件,在內存泄漏很厲害時,app 瀕臨崩潰時再抓取一個hprof 文件。

對比看這兩個圖,就很容易看出來上面的餅圖中哪一塊存在內存泄漏。

有的時候能直接看出來多了一塊。那麼咱們就從那一塊入手進行分析。比較快能獲得結果。

tip2:

看 dominator_tree,能夠從列表中 data_object 最多的幾項數據入手分析,以下文件所示(136,80對應的兩項)

 

我這邊曾經就由於在 onStart 中添加了一個 PhoneStateListener 的監聽,而在 onStop 中未設置爲空,致使內存泄漏。

這裏引用一點別人總結的實例:

 

緣由1:

          BraodcastReceiver,ContentObserver,FileObserver,Cursor在Activity onDeatory或者某類聲明週期結束以後必定要unregister或者close掉,不然這個Activity類會被system強引用,不會被內存回收。

緣由2:

        不要直接對Activity進行直接引用做爲成員變量,若是不得不這麼作,請用private WeakReference mActivity來作,相同的,對於Service等其餘有本身聲明週期的對象來講,直接引用都須要謹慎考慮是否會存在內存泄露的可能。()

 

[java]  view plain copy
 
  1. private static class MyHandler extends Handler {  
  2.         private WeakReference<GeneralSettings> mStatus;  
  3.   
  4.         public MyHandler(GeneralSettings activity) {  
  5.             mStatus = new WeakReference<GeneralSettings>(activity);  
  6.         }  
  7.   
  8.         @Override  
  9.         public void handleMessage(Message msg) {  
  10.             GeneralSettings status = mStatus.get();  
  11.             if (status == null) {  
  12.                 return;  
  13.             }  
  14.   
  15.             switch (msg.what) {  
  16.                 case EVENT_UPDATE_STATS:  
  17.                     status.updateTimes();  
  18.                     sendEmptyMessageDelayed(EVENT_UPDATE_STATS, 1000);  
  19.                     break;  
  20.             }  
  21.         }  
  22. }  
[java]  view plain  copy
 
  1. private static class MyHandler extends Handler {  
  2.         private WeakReference<GeneralSettings> mStatus;  
  3.   
  4.         public MyHandler(GeneralSettings activity) {  
  5.             mStatus = new WeakReference<GeneralSettings>(activity);  
  6.         }  
  7.   
  8.         @Override  
  9.         public void handleMessage(Message msg) {  
  10.             GeneralSettings status = mStatus.get();  
  11.             if (status == null) {  
  12.                 return;  
  13.             }  
  14.   
  15.             switch (msg.what) {  
  16.                 case EVENT_UPDATE_STATS:  
  17.                     status.updateTimes();  
  18.                     sendEmptyMessageDelayed(EVENT_UPDATE_STATS, 1000);  
  19.                     break;  
  20.             }  
  21.         }  
  22. }  

 

緣由3:

對 Context 保持了一個長生命週期的引用。

 

[java]  view plain copy
 
  1. private static Drawable sBackground;  
  2. @Override  
  3. protected void onCreate(Bundle state) {  
  4.   super.onCreate(state);  
  5.   TextView label = new TextView(this);  
  6.   label.setText("Leaks are bad");  
  7.   if (sBackground == null) {  
  8.     sBackground = getDrawable(R.drawable.large_bitmap);  
  9.   }  
  10.   label.setBackgroundDrawable(sBackground);  
  11.   setContentView(label);  
  12. }  
[java]  view plain  copy
 
  1. private static Drawable sBackground;  
  2. @Override  
  3. protected void onCreate(Bundle state) {  
  4.   super.onCreate(state);  
  5.   TextView label = new TextView(this);  
  6.   label.setText("Leaks are bad");  
  7.   if (sBackground == null) {  
  8.     sBackground = getDrawable(R.drawable.large_bitmap);  
  9.   }  
  10.   label.setBackgroundDrawable(sBackground);  
  11.   setContentView(label);  
  12. }  


sBackground的生命週期比Activity要長,label引用到context,sBackground又把label設爲內部成員變量,因此sBackground引用到了context,致使activity結束的時候context仍是不能釋放,從而引起內存泄露。(不甚理解,還要仔細研究一下)

總結:

1.      對activity的引用應該控制在activity的生命週期以內;

2.      若是不能就考慮使用getApplicationContext或者getApplication;

3.      儘可能不要在靜態變量或者靜態內部類中使用非靜態外部成員變量(包括context),即便要使用,也要考慮適時把外部成員變量置空(如上例能夠經過把sBackground的callback置空來解決內存泄露的問題);也能夠在內部類中使用弱引用來引用外部類的變量;

4.      作到在onDestroy中釋放資源,如清空對圖片等資源有直接引用或者間接引用的數組(使用array.clear();array = null);

相關文章
相關標籤/搜索