Android 記憶體泄漏檢測 LeakCanary 介紹及使用
官網點擊這里 ,本文是對官網的大致翻譯和加上自己的理解
一、使用🌚
使用 LeakCanary ,需要向 app 模塊中的 build.gradle 檔案中,添加:
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}
確認 LeakCanary 在啟動的時候運行了可以在日志(Logcat)中查看是否有一下的輸出:
D LeakCanary: LeakCanary is running and ready to detect leaks
目前,LeakCanary 回自動檢測一下型別的記憶體泄漏:
- 銷毀的 Activity 實體
- 銷毀的 Fragment 實體
- 銷毀的 fargment view 實體
- 清除過的 ViewModel 實體
二、介紹🌘
LeakCanary 是的作業原理和如何檢測和修復記憶體泄漏 Bug,
什么是記憶體泄漏?
在 Java 運行時,記憶體泄漏就是指編程錯誤導致應用參考了一個不再需要的 Object,結果那個物件所占用的記憶體不能被回收,最終導致 OutOfMemoryError (OOM),程式崩潰
一般導致記憶體泄漏的原因
大部分導致記憶體泄漏的錯誤與物件的生命周期有關, 舉幾個栗子:
- 添加一個 Fragment 實體備用,但是卻沒有在 Fragment.onDestoryView() 中清除視圖欄位(view fields)
- 一個 Context 欄位指向一個 Activity 實體,在應用配置發生變化的時候(例如螢屏旋轉)參考的 Activity 實體會重建
- 向一個有生命周期的物件注冊(register)了 listener、broadcast recevier 或 RxJava subscription,結果在物件生命周期結束的時候忘記注銷(unregister)
三、LeakCanary 作業原理🌗
作業分為4步:
- 檢測遺留的物件
- 轉存堆記憶體
- 分析堆
- 檢測出記憶體泄漏
1、檢測遺留的物件
LeakCanary 使用鉤子(hooks)織入 android 的生命周期,自動檢測活動和碎片什么時候銷毀、什么時候被回收,這些銷毀的物件會被以軟參考(weak references)的方式傳到一個 Objectwatcher 物件中,通過下面的方式可以看到不在需要的物件,例如下面的不適用的視圖物件:
AppWatcher.objectWatcher.watch(myDetachedView, "View was detached")
如果 Objectwatcher 的弱參考在5秒并且運行了垃圾回收后沒有被清除,則認為這個物件被保留并且有可能導致記憶體泄漏,并且會列印類似下面的日志:
D LeakCanary: Watching instance of com.example.leakcanary.MainActivity
(Activity received Activity#onDestroy() callback)
... 5 seconds later ...
D LeakCanary: Scheduling check for retained objects because found new object
retained
LeakCanary 等到保留的物件達到一個閾值后進行堆記憶體的轉存,并且會用通知提醒目前的個數,點擊通知會立即進行堆記憶體的轉存,關于閾值的設定,應用在前臺的時候默認閾值是5,在后臺(不可見)的時候閾值為1,
2、堆記憶體轉存
按上文所說,遺留的物件到閾值后,LeakCanary 就會把Java的堆記憶體轉成一個后綴為 .hprof 的檔案存盤到檔案系統中,檔案的存盤需要獲得 android.permission.WRITE_EXTERNAL_STORAGE 權限,存盤到叫 leakcanary-com.example 的檔案夾中,其中 com.example 是一你敢用的包名,在存的程序中應用會暫停運行一會,同時 LeakCanary 會給出提示,
3、分析堆記憶體
存盤的 .hprof 檔案會通過一個庫在記憶體中定位到仍保留的物件,(這個不是我們要關心的),我們要關心的是對每個遺留物件,LeakCanary 都會找到一個阻止物件被回收的參考路徑叫 leak trace,分析完畢 LeakCanary 會將結果列印在 Logcat 中,如下,LeakCanary 會對每個 leak trace 生成一個簽名,然后對leak根據簽名分組,每組 leaks 產生的都是因為相同的錯誤,
====================================
HEAP ANALYSIS RESULT
====================================
2 APPLICATION LEAKS
Displaying only 1 leak trace out of 2 with the same signature
Signature: ce9dee3a1feb859fd3b3a9ff51e3ddfd8efbc6
┬───
│ GC Root: Local variable in native code
│
...
LeakCanary 會生在應用串列中生成一個啟動圖示,每個相關的參考都會被在下面用一個紅線標識,當使用文本分享的時候就類似于下面這樣,用~~~~標識:
...
│
├─ com.example.leakcanary.LeakingSingleton class
│ Leaking: NO (a class is never leaking)
│ ↓ static LeakingSingleton.leakedViews
│ ~~~~~~~~~~~
├─ java.util.ArrayList instance
│ Leaking: UNKNOWN
│ ↓ ArrayList.elementData
│ ~~~~~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ ↓ Object[].[0]
│ ~~~
├─ android.widget.TextView instance
│ Leaking: YES (View.mContext references a destroyed activity)
...
4、檢測記憶體泄漏
LeakCanary 將泄漏分為應用泄漏( Application Leaks)和三方庫泄漏(Library Leaks),第三方庫導致的記憶體泄漏能被標識出來,影響到自己的應用,但是這個問題不是我們能控制,當然也可以使用反射或者向作者報告這種錯誤,
四、解決記憶體泄漏問題🌖
這個程序也分四步:
1. 找到 leak trace
2. 縮小懷疑的參考的范圍
3. 找到導致泄漏的原因
4. 在代碼層面修復 bug
1、找到泄漏的參考路徑
介紹GC root 概念,GC roots 是標識那些總是可達的物件,就表示他們是不能被回收的,主要包括以下四類:
1、本地變數,屬于一個執行緒
2、正在運行中的Java 執行緒的實體
3、系統類,從不被卸載
4、本地方法參考,是被本地方法的代碼控制
2、縮小可疑范圍
在一個泄漏路徑中,開始的時候所有的參考都是被懷疑的物件, LeakCanary 能自動縮小可疑范圍,具體程序就是,先標記所有的參考物件,然后在路徑上從上到下,一個一個檢測,物件是能被回收,如果是導致泄漏的,則會在物件前面加上一句LEAKING:YES 原因,如下:
┬───
│ GC Root: System class
│
├─ android.provider.FontsContract class
│ ↓ static FontsContract.sContext
├─ com.example.leakcanary.ExampleApplication instance
│ Leaking: NO (Application is a singleton)
│ ↓ ExampleApplication.leakedViews
│ ~~~~~~~~~~~
├─ java.util.ArrayList instance
│ ↓ ArrayList.elementData
│ ~~~~~~~~~~~
├─ java.lang.Object[] array
│ ↓ Object[].[0]
│ ~~~
├─ android.widget.TextView instance
│ Leaking: YES (View.mContext references a destroyed activity)
│ ↓ TextView.mContext
╰→ com.example.leakcanary.MainActivity instance
3、根據縮小的范圍找到導致泄漏的參考
4、找到以后修復錯誤🌞
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/291223.html
標籤:其他
