Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> 使用Android studio分析內存洩露

使用Android studio分析內存洩露

編輯:關於android開發

使用Android studio分析內存洩露


 

使用Android studio分析內存洩露

This post is a permitted translation of badoo Tech Blog and I add some text and screenshots for android studio users.
Origin Author: Dmytro Voronkevych
follow badoo on Tweet
Translator: Miao1007

截至androidstudio1.3為止,其內部的MemoryDump功能都很難使用,還是使用MAT更佳。

Android使用java作為平台開發,幫助了我們解決了很多底層問題,比如內存管理,平台依賴等等。然而,我們也經常遇到OutOfMemoey問題,垃圾回收到底去哪了?

接下來是一個Handler Leak的例子,它一般會在編譯器中被警告提示。

所需要的工具

Android Studio 1.1 or higher Eclipse MemoryAnalyzer

示例代碼

public class NonStaticNestedClassLeakActivity extends ActionBarActivity {

  TextView textView;

  public static final String TAG = NonStaticNestedClassLeakActivity.class.getSimpleName();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_non_static_nested_class_leak);
    textView = (TextView)findViewById(R.id.textview);
    Handler handler = new Handler();

    handler.postDelayed(new Runnable() {
      @Override public void 
        textView.setText(Done);
      }//a mock for long time work
    }, 800000L);


  }
}

這是一個非常基礎的Activity.注意這個匿名的Runnable被送到了Handler中,而且延遲非常的長。現在我們運行這個Activity,反復旋轉屏幕,然後導出內存並分析。

導入 Memory 到Eclipse MemoryAnalyzer

使用Androidstudio導出 heap dump

 


Android Studio dump Memory Analyze 點擊左下角的Android 選中你的程序的包名 點擊 initiates garbage collection on selected vm 點擊 dump java heap for selected client

打開MAT,進行分析

MAT是對java heap中變量分析的一個工具,它可以用於分析內存洩露。

點擊OQL圖標 在窗口輸入select * from instanceof android.app.Activity並按Ctrl + F5或者!按鈕 奇跡出現了,現在你發現洩露了許多的activity 這個真是相當的不容樂觀,我們來分析一下為什麼GC沒有回收它

 


EMA

在OQL(Object Query Language)窗口下輸入的查詢命令可以獲得所有在內存中的Activities,這段查詢代碼是不是非常簡單高效呢?

點擊一個activity對象,右鍵選中Path to GC roots

 


GC root

 


Message in looper hold a reference to Activity

在打開的新窗口中,你可以發現,你的Activity是被this$0所引用的,它實際上是匿名類對當前類的引用。this$0又被callback所引用,接著它又被Message中一串的next所引用,最後到主線程才結束。

任何情況下你在class中創建非靜態內部類,內部類會(自動)擁有對當前類的一個強引用。

一旦你把Runnable或者Message發送到Handler中,它就會被放入LooperThread的消息隊列,並且被保持引用,直到Message被處理。發送postDelayed這樣的消息,你輸入延遲多少秒,它就會洩露至少多少秒。而發送沒有延遲的消息的話,當隊列中的消息過多時,也會照成一個臨時的洩露。

嘗試使用static inner class來解決

現在把Runnable變成靜態的class

 


StaticClass

現在,搖一搖手機,導出內存

 


StaticClass_memory_analyze

為什麼又出現了洩露呢?我們看一看Activities的引用.

 


StaticClass_memory_analyze_explained

看到下面的mContext的引用了嗎,它被mTextView引用,這樣說明,使用靜態內部類還遠遠不夠,我們仍然需要修改。

使用弱引用 + static Runnable

現在我們把剛剛內存洩露的罪魁禍首 - TextView改成弱引用。

 


StaticClassWithWeakRef_code

再次注意我們對TextView保持的是弱引用,現在讓它運行,搖晃手機

小心地操作WeakReferences,它們隨時可以為空,在使用前要判斷是否為空.

 


StaticClassWithWeakRef_memory_analyze

哇!現在只有一個Activity的實例了,這回終於解決了我們的問題。

所以,我們應該記住:

使用靜態內部類 Handler/Runnable的依賴要使用弱引用。

如果你把現在的代碼與開始的代碼相比,你會發現它們大不相同,開始的代碼易懂簡介,你甚至可以腦補出運行結果。

而現在的代碼更加復雜,有很多的模板代碼,當把postDelayed設置為一個短時間,比如50ms的情況下,寫這麼多代碼就有點虧了。其實,還有一個更簡單的方法。

onDestroy中手動控制聲明周期

Handler可以使用removeCallbacksAndMessages(null),它將移除這個Handler所擁有的RunnableMessage

//Fixed by manually control lifecycle
  @Override protected void onDestroy() {
    super.onDestroy();
    myHandler.removeCallbacksAndMessages(null);
  }

現在運行,旋轉手機,導出內存

 


removeCallbacks_memory_analyze

Good!只有一個實例。

這樣寫可以讓你的代碼更加簡潔與可讀。唯一要記住的就是就是要記得在生命周期onDestory的時候手動移除所有的消息。

使用WeakHander

(這個是第三方庫,我就不翻譯了,大家去Github上去學習吧)

結論

在Handler中使用postDelayed需要額外的注意,為了解決問題,我們有三種方法

使用靜態內部Handler/Runnable + 弱引用 在onDestory的時候,手動清除Message 使用Badoo開發的第三方的 WeakHandler

這三種你可以任意選用,第二種看起來更加合理,但是需要額外的工作。第三種方法是我最喜歡的,當然你也要注意WeakHandler不能與外部的強引用共同使用。

 



 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved