android 中如何分析記憶體流失

來源:互聯網
上載者:User

標籤:

前提條件:

1,電腦安裝了java 運行環境  

2,手機端開啟了 USB 調試開關 

3,Root

4,安裝MAT工具,:http://www.eclipse.org/mat/downloads.php

基本步驟:

1,使用eclipse 內建的 DDMS 工具分析各線程的記憶體使用量情況,如所示

Heap視圖介面會定時重新整理,在對應用的不斷的操作過程中就可以看到記憶體使用量的變化。

怎樣判斷當前進程是否有記憶體流失呢?

這裡需要注意一個值:VM Heap頁面中部有一個data object選項,即資料對象,也就是我們的程式中大量存在的類類型的對象。

在data object一行中有一列是“Total Size”,其值就是當前進程中所有Java資料對象的記憶體總量,一般情況下,這個值的大小決定了是否會有記憶體流失。如中選中行所示。

可以據此判斷記憶體有泄漏:
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 開啟,不知道 Google在這裡

搗了什麼鬼,難道是最佳化?

使用 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. }  

 

原因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. }  


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);

5.      線程一定要管理好,在開發中碰到的很多記憶體流失的原因是由於線程未及時關閉,每一次操作都會重新建立一個此線程造成的;

6.      慎用靜態變數,一些臨時類中使用靜態變數容易導致此類對象無法釋放進而造成記憶體流失;

android 中如何分析記憶體流失

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.