它是什么
一句話說驶臊,就是:
Android項目中豪治,用于檢測內(nèi)存泄露,優(yōu)化性能的工具
解決了什么問題
解決了,內(nèi)存泄漏難以發(fā)現(xiàn)的問題讼积。通過Leakcanary肥照,可以輕松的找到GC中跟搜索法可達,然而不再使用的對象勤众。
一般情況下舆绎,這種問題是很難被發(fā)現(xiàn)的,原因是他的出現(xiàn)是不經(jīng)意間的们颜,其中吕朵,內(nèi)存泄露常見的場景有:
內(nèi)存泄露常見場景
非靜態(tài)內(nèi)部類的靜態(tài)實例
- 非靜態(tài)內(nèi)部類會持有外部類的引用
- 在外部類生命周期結(jié)束后,靜態(tài)實例會長期維持著外部類的引用窥突,導致無法被gc
多線程相關(guān)的匿名內(nèi)部類\非靜態(tài)內(nèi)部類
- 匿名內(nèi)部類同樣也會持有外部類實例的引用努溃,例如(AsyncTask、Tread阻问、Runnable接口的實現(xiàn)類)
- 匿名內(nèi)部類\非靜態(tài)內(nèi)部類中有耗時操作梧税,在外部類生命周期結(jié)束后,仍然長時間維持著外部類的引用称近,導致無法被gc
Handler內(nèi)存泄露
- Handler定義為非靜態(tài)的第队,即持有了外部引用
- Message存儲到MessageQueue中,耗時較長刨秆,無法被回收
怎樣去使用它
集成進項目之中
-
新版本的Leakcanary只需添加依賴,無需更改代碼
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
發(fā)現(xiàn)內(nèi)存泄露
Leaknary 能夠發(fā)現(xiàn)內(nèi)存泄露的位置有:
- 銷毀的
Activity
實例 - 銷毀的
Fragment
實例 - 銷毀的
View
實例 - 銷毀的
ViewModel
實例
LeakCanary可以hook到Android生命周期中凳谦,從而自動檢測Activity和Fragment何時destroy,并進行垃圾收集衡未。ObjectWatcher持有這些被destroy的對象的弱引用尸执。通過以下方法,可以找到被Destroy的對象眠屎,例如
AppWatcher.objectWatcher.watch(myDetachedView, "View was detached")
如果運行垃圾回收剔交,并等待5秒鐘后ObjectWatcher仍
未清除 肆饶,則認定可能發(fā)生內(nèi)存泄漏改衩。LeakCanary將此記錄到Logcat:
在未被正常清楚的對象達到一定數(shù)量后,LeakCanary將其dump到存儲堆驯镊,并顯示通知:
注意:
當應用程序的狀態(tài)是可見的時候,默認閾值為5 板惑,當應用程序是不可見的時候橄镜,默認閾值為1,冯乘。如果看到Leakcanary彈出通知洽胶,然后將應用程序壓至后臺(例如,直接上劃進入主頁)裆馒,則LeakCanary閾值從5更改為1姊氓,并在5秒鐘后dump到存儲堆丐怯。若點擊按鈕將強制LeakCanary立即dump到存儲堆。
打印堆中的信息
當保留對象的數(shù)量達到閾值時翔横,LeakCanary將Java堆dump到存儲在Android文件系統(tǒng)上的.hprof文件中(詳見LeakCanary在何處存儲堆轉(zhuǎn)儲读跷?)。轉(zhuǎn)儲堆會使應用程序停止運行一小段時間禾唁,在此期間LeakCanary會顯示Toast:
分析堆中的信息
LeakCanary.hprof使用Shark解析文件效览,并在該堆存儲中找到保留的對象。
對于每個保留的對象丐枉,LeakCanary會查找引用路徑,以防止對該保留的對象進行垃圾回收:至于泄露跟蹤的具體內(nèi)容掘托,將在下一部分中展開:修復內(nèi)存泄漏矛洞。
分析完成后,LeakCanary將顯示帶有摘要的通知烫映,并在Logcat中顯示結(jié)果沼本。注意:下圖將4個保留的對象歸為2個不同的泄漏。LeakCanary為每個泄漏跟蹤創(chuàng)建一個簽名锭沟,并將具有相同簽名的泄漏(即抽兆,由同一bug引起的泄漏)組合在一起。
點擊彈出的通知辫红,將提供更多詳細的信息。也可以通過啟動Leaks來找到該條內(nèi)存泄露的詳細信息
在Leaks中贴妻,每行對應一組具有相同簽名的泄漏。應用程序首次出現(xiàn)的內(nèi)存泄露類型將標記為“New”蝙斜。
點擊泄漏將展示整個泄露的跟蹤信息。
泄漏簽名是每個級聯(lián)的散列參考懷疑導致泄漏孕荠,即娩鹉,每個參考與紅色下劃線顯示:
當泄漏跟蹤以文本形式共享時稚伍,這些相同的可疑引用都帶有下劃線:如
并計算出響應的signature 弯予,例如上述的簽名為
分類內(nèi)存泄露信息
常見的內(nèi)存泄露Leaknary將其分成了第三方庫和程序員操作失誤兩種。
-
第三方庫的泄露如下所示:(帶有Library Leak)
圖10.LeakCanary發(fā)現(xiàn)了Library Leak个曙。 -
程序員異常操作的泄露:
圖10.NewsFragment.mRootV處泄露
如何解決內(nèi)存泄露
- 我們在代碼中能不用static變量持有context就不用锈嫩,非要用就用weak引用。
- 對于內(nèi)部類,盡量用靜態(tài)內(nèi)部類呼寸,這樣就不會持有外部類引用那槽。如果需要外部類引用做一些事,就手動賦給一個weak引用等舔。
- 對于匿名內(nèi)部類骚灸,不要圖簡單方便,實在不行就 寫成外部類
- 在使用handler時慌植,記得在activity的onDestroy()中加上remove()甚牲,有待嘗試