Android檢測記憶體洩漏之leakcanary

Dusan_杜小菜發表於2016-05-24

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

一,大話記憶體洩漏

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。如圖:
activity的靜態引用造成洩漏

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

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

五,總結

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

1,使用Application的context

需要上下文的時候,如果不是非得activity物件,傳入Application的context,因為Application的context的生命週期比Activity長,它是app全域性的,相當於static的生命週期。

2,static變數不要引用view例項

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


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

相關文章