LeakCanary:檢測所有的內(nèi)存泄漏
原文: LeakCanary: Detect all memory leaks!
- ava.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的注冊過程中,我們在bitmap上 繪制了一個用戶的簽名。這個bitmap和設(shè)備的屏幕大小相當(dāng),在創(chuàng)建它的時候,我遇到了相當(dāng)數(shù)量的OOM導(dǎo)致的崩潰。
我們試過了幾種方法,沒有一個解決了我們的問題:
-
使用Bitmap.Config.ALPHA_8(簽名是不需要顏色的)
-
捕獲OutOfMemoryError,觸發(fā)垃圾回收然后重試幾次(從GCUtils 獲得的啟發(fā))
-
我們沒有考慮過將bitmap分配在堆內(nèi)存之外,那個時候Fresco 還沒出現(xiàn)。
我們看待問題的方式是不對的
bitmap的大小本身不是什么問題。當(dāng)內(nèi)存快要滿了的時候,OOM隨時隨地都可能發(fā)生。尤其是在創(chuàng)建大對象的時候更容易發(fā)生,比如bitmap。OOM一般代表著更深層次的問題:內(nèi)存泄漏。
什么是內(nèi)存泄漏?
有些對象只有有限的生命周期。當(dāng)它們的任務(wù)完成之后,它們將被垃圾回收。如果在對象的生命周期本該結(jié)束的時候,這個對象還被一系列的引用,這就會導(dǎo)致內(nèi)存泄漏。隨著泄漏的累積,app將消耗完內(nèi)存。
比如,在Activity.onDestroy()被調(diào)用之后,view樹以及相關(guān)的bitmap都應(yīng)該被垃圾回收。如果一個正在運行的后臺線程繼續(xù)持有這個Activity的引用,那么相關(guān)的內(nèi)存將不會被回收,這最終將導(dǎo)致OutOfMemoryError崩潰。
尋找內(nèi)存泄漏
尋找內(nèi)存泄漏是一個人工操作的過程,在Raizlabs的 Wrangling Dalvik 系列中描述得很清楚。
下面是關(guān)鍵的步驟:
-
通過 Bugsnag, Crashlytics, 或者 Developer Console了解OOM
-
主動重現(xiàn)問題。你可能需要買或者借或者偷一個遭遇了崩潰的特殊設(shè)備(并不是所有的設(shè)備上都會發(fā)生內(nèi)存泄漏!)。你還需要找出是什么串在一起導(dǎo)致了內(nèi)存泄漏。
-
當(dāng)OOM出現(xiàn)時進(jìn)行堆轉(zhuǎn)儲(dump the heap)(這里介紹了如何做).
-
使用MAT或YourKit內(nèi)存檢測工具檢測內(nèi)存的變化,并找出哪個對象應(yīng)該被垃圾回收;
-
從那個對象到GC roots推斷最短的強引用路徑;
-
在路徑中找出不存在的引用,并修復(fù)memory leak;
要是有一個庫可以為你做完所有的事情,讓你專注于修復(fù)內(nèi)存泄漏的問題,那該有多好。
LeakCanary介紹
LeakCanary 是一個開源的在debug版本中檢測內(nèi)存泄漏的java庫。
讓我們來看看一個cait的例子:
- 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;
創(chuàng)建一個RefWatcher實例,然后給它一個對象讓它觀察:
- // We expect schrodingerCat to be gone soon (or not), let's watch it.
- refWatcher.watch(schrodingerCat);
當(dāng)檢測出泄漏的時候,你會自動得到一個漂亮的泄漏線索:
- * GC ROOT static Docker.container
- * references Box.hiddenCat
- * leaks Cat instance
我們知道你的時間寶貴,因此我們讓它非常好設(shè)置。只需幾行代碼,LeakCanary就能自動檢測Activity的泄漏:
|
|
當(dāng)內(nèi)存不足時,會有一個通知和良好的展示界面:
結(jié)論
在啟用LeakCanary之后,我們發(fā)現(xiàn)和修復(fù)了許多內(nèi)存泄漏的問題。我們甚至發(fā)現(xiàn)了一些 Android SDK中的泄漏。
結(jié)果是非常令人驚奇的,我們現(xiàn)在減少了94%的oom崩潰問題。
如果你想終結(jié)OOM崩潰,現(xiàn)在就安裝LeakCanary!