天天看點

Android檢測記憶體洩漏之leakcanary

記憶體洩漏,memory leak,開發者經常念叨的一個詞,稍不留意,就遊走在我們的代碼中。Andriod開發,記憶體洩漏的原因有很多,比如activity的context引用,static引用,廣播未取消注冊,MVP設計時沒有detachView,Rx沒有取消subscribe訂閱,動畫處理等。檢測的工具也很多。今天總結下,LeakCanary的使用。看這圖,Js接口引用activity洩漏了528kb。

Android檢測記憶體洩漏之leakcanary

一,大話記憶體洩漏

Java通過垃圾收集器(GC, garbage collection)來自動管理記憶體。當一個對象不再被使用,就會被自動回收。而“記憶體洩漏“就是沒有成功回收記憶體的展現。一個對象已經不需要再使用了,但是因為其它的對象持有該對象的引用,導緻它的記憶體不能被回收。“記憶體洩漏”積累到一定程度,可能導緻OOM,是以在寫代碼的過程中,要注意導緻“記憶體洩漏”的代碼寫法,提高代碼的健壯性。

二,記憶體洩漏的類型

如果一個對象是可達的有引用的,但實際上它已經沒有再使用了,但引用它的對象依然存在,這樣的它就是記憶體洩漏的對象。記憶體洩漏可分為以下幾種類型:

1、靜态變量引起的記憶體洩漏

通常是,一個靜态變量,持有對象的應用,對象銷毀了,static修飾的變量還在,導緻記憶體無法回收。我之前在BaseActivity中加入實作類到集合中,就造成過記憶體洩漏。如過靜态context一直持有activity的引用,onDestory執行後造成記憶體洩漏。

public class LeakActivity extends Activity {
    //靜态context
    private static Context sContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leak);
        sContext = this;//指派後靜态context一直持有activity的引用,onDestory執行後造成記憶體洩漏。
    }
}
           

我遇到的情況,activity當上下文傳,js接口引用webview所在的activity,反正不要靜态的引用。

2、非靜态内部類引起的記憶體洩漏

如果用android studio開發,寫handle發送消息,下面的寫法會有黃色警告,因為可能會引發記憶體洩漏:

private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};
           

為什麼呢?非靜态内部類會引用外部類對象(Activity),當它使用了 postDelayed 的時候,如果 Activity 已經 finish 了,而這個 handler 仍然引用着這個 Activity 就會緻使記憶體洩漏,因為這個 handler 會在一段時間内繼續被 mainLooper 持有,導緻引用仍然存在,在這段時間内,如果記憶體不夠使,可能OOM了。

3、資源未關閉引起的記憶體洩漏

IO流的操作,切記要close關閉流,檔案讀寫,網絡通路等都要及時關閉位元組流、字元流;注冊了廣播要在onDestory方法中銷毀。使用了BraodcastReceiver、Cursor、Bitmap等資源時,需要及時釋放掉,若沒有釋放,則會引起記憶體洩漏。這個比較好了解。

三,記憶體洩漏檢測工具

用Eclipse開發自帶的記憶體檢測工具:Heap。

DDMS中的Heap工具用于大緻分析是否存在“記憶體洩漏”,而MAT工具則用于分析“記憶體洩漏”發生在哪裡,MAT工具中,File->Open Heap Dump,可以打開xxx.hprof檔案分析。這個我現在不用。不多說。直接看下面介紹的LeakCanary。

四,LeakCanary,記憶體洩漏精确檢測神器。

要精确地追蹤到記憶體洩漏點,強烈推薦使用Square 公司開源的 LeakCanary開源方案,LeakCanary在Application實作類中一行代碼,簡單暴力侵入性地捕獲記憶體洩漏代碼,甚至捕獲Android元件的記憶體洩漏代碼。我發現android動畫繪制的時候存在記憶體洩漏的問題。

1,LeakCanary就像金絲雀監視着你的“煤礦”

不用重複造輪子,直接拿來,android studio開撸步驟:

//在module的build.gradle中:
 dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
   testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'

//上面是官方推薦寫法,其實你要一行也行:
 compile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'

//然後在你的Application的子類中install:
public class ExampleApplication extends Application{
  @Override public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
  }
}
           

Good,好了,LeakCanary這是金絲雀會随着你app的運作,自動安裝監測,給你通知。友善你檢視那個類出現了記憶體洩漏,甚至告訴你洩漏了多少M。如圖:

Android檢測記憶體洩漏之leakcanary

2,檢視log日志,分析LeakCanary收集的資料

一般通過它的通知界面就很明了了,如果要詳細看看過程,看log部分如下:SplashActivity has leaked!

Android檢測記憶體洩漏之leakcanary

五,總結

遇到記憶體洩漏不是什麼好事,是以平時寫代碼,留個心眼,以預防為主。一些通用的解決方案:

1,使用Application的context

需要上下文的時候,如果不是非得activity對象,傳入Application的context,因為Application的context的生命周期比Activity長,它是app全局的,相當于static的生命周期。

2,static變量不要引用view執行個體

3,關閉資源,close,unsubscribe,unregister,null記得吃藥。

歡迎交流,Dusan,杜乾,291902259!OpenDeveloper!