LeakCanary: 讓內存泄露無所遁形

文章:http://www.jianshu.com/p/7db231163168html

github:https://github.com/square/leakcanaryjava

本文爲LeakCanary: Detect all memory leaks!的翻譯。原文在: https://corner.squareup.com/2015/05/leak-canary.htmlandroid

java.lang.OutOfMemoryError
        at android.graphics.Bitmap.nativeCreate(Bitmap.java:-2)
        at android.graphics.Bitmap.createBitmap(Bitmap.java:689)
        at com.squareup.ui.SignView.createSignatureBitmap(SignView.java:121)

誰也不會喜歡 OutOfMemoryError

在 Square Register 中, 在簽名頁面,咱們把客戶的簽名畫在 bitmap cache 上。 這個 bitmap 的尺寸幾乎和屏幕的尺寸同樣大,在建立這個 bitmap 對象時,常常會引起 OutOfMemoryError,簡稱OOMgit

當時,咱們嘗試過一些解決方案,但都沒解決問題github

  • 使用 Bitmap.Config.ALPHA_8 由於,簽名僅有黑色。app

  • 捕捉 OutOfMemoryError, 嘗試 GC 並重試(受 GCUtils 啓發)。eclipse

  • 咱們沒想過在 Java heap 內存以外建立 bitmap 。苦逼的咱們,那會 Fresco 還不存在。ide

路子走錯了

其實 bitmap 的尺寸不是真正的問題,當內存吃緊的時候,處處都有可能引起 OO。在建立大對象,好比 bitmap 的時候,更有可能發生。OOM 只是一個表象,更深層次的問題多是: 內存泄露工具

什麼是內存泄露

一些對象有着有限的生命週期。當這些對象所要作的事情完成了,咱們但願他們會被回收掉。可是若是有一系列對這個對象的引用,那麼在咱們期待這個對象生命週期結束的時候被收回的時候,它是不會被回收的。它還會佔用內存,這就形成了內存泄露。持續累加,內存很快被耗盡。post

好比,當 Activity.onDestroy 被調用以後,activity 以及它涉及到的 view 和相關的 bitmap 都應該被回收。可是,若是有一個後臺線程持有這個 activity 的引用,那麼 activity 對應的內存就不能被回收。這最終將會致使內存耗盡,而後由於 OOM 而 crash。

對戰內存泄露

排查內存泄露是一個全手工的過程,這在 Raizlabs 的 Wrangling Dalvik 系列文章中有詳細描述。

如下幾個關鍵步驟:

  1. 經過 BugsnagCrashlytics 或者 Developer Console 等統計平臺,瞭解 OutOfMemoryError 狀況。

  2. 重現問題。爲了重現問題,機型很是重要,由於一些問題只在特定的設備上會出現。爲了找到特定的機型,你須要想盡一切辦法,你可能須要去買,去借,甚至去偷。 固然,爲了肯定復現步驟,你須要一遍一遍地去嘗試。一切都是很是原始和粗暴的。

  3. 在發生內存泄露的時候,把內存 Dump 出來。具體看這裏

  4. 而後,你須要在 MAT 或者 YourKit 之類的內存分析工具中反覆查看,找到那些本來該被回收掉的對象。

  5. 計算這個對象到 GC roots 的最短強引用路徑。

  6. 肯定引用路徑中的哪一個引用是不應有的,而後修復問題。

很複雜對吧?

若是有一個類庫能在發生 OOM 以前把這些事情所有都搞定,而後你只要修復這些問題就行了,豈不妙哉!

LeakCanary

LeakCanary 是一個檢測內存泄露的開源類庫。你能夠在 debug 包種輕鬆檢測內存泄露。

先看一個例子:

class Cat {
}

class Box {
  Cat hiddenCat;
}
class Docker {
    // 靜態變量,將不會被回收,除非加載 Docker 類的 ClassLoader 被回收。
    static Box container;
}

// ...

Box box = new Box();

// 薛定諤之貓
Cat schrodingerCat = new Cat();
box.hiddenCat = schrodingerCat;
Docker.container = box;

建立一個RefWatcher,監控對象引用狀況。

// 咱們期待薛定諤之貓很快就會消失(或者不消失),咱們監控一下
refWatcher.watch(schrodingerCat);

當發現有內存泄露的時候,你會看到一個很漂亮的 leak trace 報告:

  • GC ROOT static Docker.container
  • references Box.hiddenCat
  • leaks Cat instance

咱們知道,你很忙,天天都有一大堆需求。因此咱們把這個事情弄得很簡單,你只須要添加一行代碼就好了。而後 LeakCanary 就會自動偵測 activity 的內存泄露了。

public class ExampleApplication extends Application {
  @Override public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
  }
}

而後你會在通知欄看到這樣很漂亮的一個界面:

結論

使用 LeakCanary 以後,咱們修復了咱們 APP 中至關多的內存泄露。咱們甚至發現了 Android SDK 中的一些內存泄露問題

結果是驚豔的,咱們減小了 94% 的由 OOM 致使的 crash。

若是你也想消滅 OOM crash,那還猶豫什麼,趕快使用 LeakCanary

相關連接:

相關文章
相關標籤/搜索