LeakCanary:檢測你APP所有的內存泄露
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)
1. 沒人喜歡OOM這個Crash
在Square註冊用戶中,我們在以屏幕大小的bitmap的緩存中繪製用戶的簽名。而當我們創建這個bitmap的時候經常會出現OOM,
我們嘗試了一些方法,但是都沒有解決。
比如:
1.使用bitmap的Bitmap.Config.ALPHA_8
2.捕獲OOM的異常,然後出發GC來回收,然後再重新創建
3.我們沒有想到去收集bitmap的java堆內存。
2.我們曾以錯誤的方式來看待它。
問題並不在bitmap的大小上,當內存快滿的時候,肯定會拋出OOM的,當你試圖創建大的對象時,會經常拋出OOM異常。而出現OOM更深層次的問題是在於內存泄露。
3.什麼是內存泄露?
一些對象有自己限制的生命週期,當他們的工作結束時,他們試圖去被gc回收,如果當生命週期結束以後內存中仍然握着這個對象的一些應用的時候,就會出現內存泄露。當內存泄露越來越多的時候,就會出現OOM。
例如, 當Activity.onDestroy()被調用的時候,activity對於的佈局,相關的bitmap對象都應該被gc回收,如果一個後臺線程在運行,並且握着activity的一個引用,那這個引用就沒法揹回收,最後就會導致OOM。
4.監聽內存泄露。
監聽內存泄露是一個手動的過程。
下面是幾個關鍵的步驟:
1.學習關於OOM的相關知識。
2.試圖去重現這個問題。 你需要去找到出現該問題的手機,你也需要去弄清楚是哪個地方出現這個問題。
3.當出現OOM的時候清空堆內存。
4.使用MAT或者其他工具找到哪些對象需要被回收。
5.計算強引用最短的從該對象到gc根部的路徑。 (什麼是 path from that object to the GC roots)
6. 找出哪些在該path下不該存在的引用,並且修復該內存泄露。
5. 介紹LeakCanary
LeakCanary 是一個開源的java類庫,去檢測在開發版本中的內存泄露。
讓我們在cat中看一個例子:
class Cat {
}
class Box {
Cat hiddenCat;
}
class Docker {
static Box container;
}
// ...
Box box = new Box();
Cat schrodingerCat = new Cat();
box.hiddenCat = schrodingerCat;
Docker.container = box;
你需要創建了一個RefWatcher 實例並且給他賦予一個對象去觀察。
// We expect schrodingerCat to be gone soon (or not), let's watch it.
refWatcher.watch(schrodingerCat);
當內存被檢測出泄露的時候,你會自動獲得一個泄露堆棧信息。
* GC ROOT static Docker.container
* references Box.hiddenCat
* leaks Cat instance
我們知道程序員都很忙,所以我們製作了一個和easy的方式去創建,只需要一行代碼,LeakCanary 就會自動檢測activity的內存泄露。
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}
你會得到一個提醒,和一個漂亮的展示界面
6.結論
啓用 LeakCanary以後,我們發現並修正了在我們的應用程序的許多內存泄漏。我們甚至發現在Android SDK中的幾個漏洞。現在,我們已經從OOM錯誤中減少94%的Crash了。