LeakCanary の簡単な紹介と使用法

1 はじめに:

LeakCanray は、Square のオープンソース Java メモリ リーク分析ツールで、開発段階で Android アプリケーションの一般的なメモリ リークを検出するために使用されます。

  • 次の 5 つの Android シナリオでメモリ リークの監視をサポートします。
    1. 破棄されたアクティビティオブジェクト (DESTROYED 状態に入る)
    2. 破棄された Fragment オブジェクトおよび Fragment View オブジェクト (DESTROYED 状態になる)
    3. ViewModel オブジェクトをクリアしました (CLEARED 状態に入ります)
    4. サービス オブジェクトの破棄 (DESTROYED 状態に入る)
    5. WindowManager から RootView オブジェクトが削除されました

1.2 使用方法

  • build.gradle に LeakCanary 依存関係を追加するだけです (LeakCanary はデフォルトで ContentProvider を使用して、非侵入的な初期化を実現します)。注: LeakCanary は開発環境でのみ使用されるツールであるため、debugImplementation依存関係の構成には忘れずに使用してください。
// 普通ContentProvider方式
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'

// 可选,使用WorkerManager多进程分析堆快照提升分析速度
debugImplementation 'com.squareup.leakcanary:leakcanary-android-process:2.12'

2. ソースコード解析

2.1 初期化

  • 1.x時代のLeakCanaryではApplication.onCreate()に初期化コードを追加する必要がありましたが、2.x以降では起動機構を利用してContentProvider間接的に初期化APIを呼び出し、非侵入的なLeakCanary初期化を実現しています。
  • 特定の場所はサブモジュール Leakcanary-object-watcher-android 内にあります。

 <application>
   <!--可以通过配置leak_canary_watcher_auto_install变量设置是否自行初始化,默认为true -->
   <provider
      android:name="leakcanary.internal.MainProcessAppWatcherInstaller"
      android:authorities="${applicationId}.leakcanary-installer"
      android:enabled="@bool/leak_canary_watcher_auto_install"
      android:exported="false"/>

 </application>
internal class MainProcessAppWatcherInstaller : ContentProvider() {
  override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    // LeakCanary初始化代码
    AppWatcher.manualInstall(application)
    return true
  }
  ...
}

 実際の初期化コードは次のとおりです。AppWatcher.manualInstall(application)

// AppWatcher.kt
/** LeakCanary初始化 */
@JvmOverloads
fun manualInstall(
  application: Application,
  retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 默认5s后进行泄漏检测
  watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
  checkMainThread()
  ...
  // 初始化 InternalLeakCanary 内部引擎
  LeakCanaryDelegate.loadLeakCanary(application)
  // 遍历五种监听器进行分别注册
  watchersToInstall.forEach {
    it.install()
  }
  ...
}

/** 创建监听集合 */
fun appDefaultWatchers(
  application: Application,
  reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
  return listOf(
    // 对应5中Android泄漏场景(Activity、Fragment和ViewModel、View、Service)
    // 传入的reachabilityWatcher均为ReachabilityWatcher的唯一实现类ObjectWatcher
    ActivityWatcher(application, reachabilityWatcher),
    FragmentAndViewModelWatcher(application, reachabilityWatcher),
    RootViewWatcher(reachabilityWatcher),
    ServiceWatcher(reachabilityWatcher)
  )
}

 Activity、Fragment、ViewModel、RootView、Serviceを監視する4つの監視クラスに分かれているので、具体的にどのように監視されているかを分析します。

2.2 4 つの主要な監視カテゴリにより、5 つの漏洩シナリオの監視を実現

2.2.1 アクティビティウォッチャー

  • ActivityWatcherは主にActivityの監視を実装します。
/** Activity的泄漏监听 */
class ActivityWatcher(
  private val application: Application,
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        // 交给objectWatcher分析
        reachabilityWatcher.expectWeaklyReachable(
          activity, "${activity::class.java.name} received Activity#onDestroy() callback"
        )
      }
    }

  override fun install() {
    // 注册监听的方式
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
  }

  override fun uninstall() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
  }
}

  • アクティビティの監視は比較的単純で、提供されているグローバル モニタリング API を直接使用しapplication.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)、アクティビティを objectWatcher に渡して各アクティビティの onDestroy で分析できます。

2.2.2 FragmentAndViewModelWatcher

  • FragmentAndViewModelWatcher は主に Fragment、Fragment view、ViewModel の監視を実装します。
/**
 * 主要负责Fragment的泄漏监听(通过Fragment.onDestroy())
 * Fragment View的泄漏监听(通过Fragment.onDestroyView())
 * ViewModel的泄漏监听(通过ViewModel.onCleared())
 */
class FragmentAndViewModelWatcher(
    private val application: Application,
    private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

    /** Fragment监测集合(主要包含几种包下的Fragment) */
    // 集合的类型是(Activity)->Unit的函数类型
    private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
        val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()

        // Android O(8.0)后使用AndroidOFragmentDestroyWatcher监听
        if (SDK_INT >= O) {
            fragmentDestroyWatchers.add(
                AndroidOFragmentDestroyWatcher(reachabilityWatcher)
            )
        }

        // androidx包下的Fragment使用AndroidXFragmentDestroyWatcher监听
        getWatcherIfAvailable(
            ANDROIDX_FRAGMENT_CLASS_NAME,
            ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
            reachabilityWatcher
        )?.let {
            fragmentDestroyWatchers.add(it)
        }

        // android.support.v4包下的Fragment使用AndroidSupportFragmentDestroyWatcher监听
        getWatcherIfAvailable(
            ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
            ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
            reachabilityWatcher
        )?.let {
            fragmentDestroyWatchers.add(it)
        }
        fragmentDestroyWatchers
    }

    private val lifecycleCallbacks =
        object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
            override fun onActivityCreated(
                activity: Activity,
                savedInstanceState: Bundle?
            ) {
                // 监听每个Activity的onActivityCreated(),再在其中进行Fragment监听
                for (watcher in fragmentDestroyWatchers) {
                    // 由于fragmentDestroyWatchers里面本身存储的是一个(Activity)->Unit的函数类型
                    // 所以这里可以直接使用watcher(activity),直接调用,参数为activity,
                    // 实际执行的是watcher中的invoke()
                    watcher(activity)
                }
            }
        }

    override fun install() {
        application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
    }

    override fun uninstall() {
        application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
    }
    ...
}

  • Fragment と Fragment View の監視は主にFragmentAndViewModelWatcherApplication.registerActivityLifecycleCallbacks(…)インターフェースを通じて Activity.onCreate() イベントを監視し、次にFragmentManager.registerFragmentLifecycleCallbacks(…)インターフェースを通じて Fragment のライフサイクルを監視します。
  • Android のバージョンと Fragment パッケージに応じて、 、 、 の 3 つの状態がありAndroidOFragmentDestroyWatcherここでは AndroidXFragmentDestroyWatcher を例に説明します。AndroidXFragmentDestroyWatcherAndroidSupportFragmentDestroyWatcher
/** Androidx包下的Fragment、FragmentView以及ViewModel监听 */
internal class AndroidXFragmentDestroyWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {

  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    override fun onFragmentCreated(
      fm: FragmentManager,
      fragment: Fragment,
      savedInstanceState: Bundle?
    ) {
      // 注册Fragment级别的ViewModel Hook
      ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
    }

    override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null) {
        // 监听FragmentView.onDestroy()将Fragment.View交给ObjectWatcher分析
        reachabilityWatcher.expectWeaklyReachable(
          view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
          "(references to its views should be cleared to prevent leaks)"
        )
      }
    }

    override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      // 监听Fragment.onDestroy()将Fragment交给ObjectWatcher分析
      reachabilityWatcher.expectWeaklyReachable(
        fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
      )
    }
  }

  override fun invoke(activity: Activity) {
    // 这段代码会在Activity.onCreate()中执行
    if (activity is FragmentActivity) {
      val supportFragmentManager = activity.supportFragmentManager
      // 注册Fragment生命周期监听
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      // 注册Activity级别的ViewModel Hook
      ViewModelClearedWatcher.install(activity, reachabilityWatcher)
    }
  }
}

  • これには、Activity.onCreate() および Fragment.onCreate() で監視のために使用される ViewModel の監視ロジックも含まれていますViewModelClearedWatcher。システムは ViewModel をグローバルに監視するためのメソッドを提供していないため、ViewModelClearedWatcher はHookメソッドを通じて実装されます。 ViewModelClearedWatcher
internal class ViewModelClearedWatcher(
  storeOwner: ViewModelStoreOwner,
  private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {

  // 直接通过反射获取ViewModelStore类中的map变量(后面改成mMap),即可获得作用域中的所有ViewModel对象
  private val viewModelMap: Map<String, ViewModel>? = try {
    val storeClass = ViewModelStore::class.java
    val mapField = try {
      storeClass.getDeclaredField("map")
    } catch (exception: NoSuchFieldException) {
      storeClass.getDeclaredField("mMap")
    }
    mapField.isAccessible = true
    @Suppress("UNCHECKED_CAST")
    mapField[storeOwner.viewModelStore] as Map<String, ViewModel>
  } catch (ignored: Exception) {
    SharkLog.d(ignored) { "Could not find ViewModelStore map of view models" }
    null
  }

  override fun onCleared() {
    // 遍历当前作用域所有ViewModel对象
    viewModelMap?.values?.forEach { viewModel ->
      // 使用ObjectWatcher.expectWeaklyReachable
      reachabilityWatcher.expectWeaklyReachable(
        viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback"
      )
    }
  }

  companion object {
    // 在storeOwner作用域实例化ViewModelClearedWatcher对象
    fun install(
      storeOwner: ViewModelStoreOwner,
      reachabilityWatcher: ReachabilityWatcher
    ) {
      val provider = ViewModelProvider(storeOwner, object : Factory {
        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel?> create(modelClass: Class<T>): T =
          // 直接在storeOwner作用域实例化ViewModelClearedWatcher对象
          ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
      })
      provider.get(ViewModelClearedWatcher::class.java)
    }
  }
}

  • ViewModel の監視は、Activity.onCreate()/Fragment.onCreate() でカスタム ViewModel (つまり、ViewModelClearedWatcher) を作成し、次に ViewModelClearedWatcher の onCleared() を監視し、対応する対応する関数を取得することであることがわかりますViewModelStore をフックして、ドメイン内のすべての ViewModel オブジェクト (アクティビティ/フラグメント) を作成し、最後に監視のために ObjectWatcher に渡します。

2.2.3 RootViewWatcher

  • RootViewWatcher は RootView のメモリ リークを処理するためのリスナーです。システムはグローバルな RootView 監視を提供していないため、LeakCannary は依然として Hook によって処理されますが、ここには Hook はありませんが、正方形の別のオープン ソース ライブラリ カーテンがそれを処理するために使用されます。Hook WMS サービスの内部WindowManagerGlobal(sdk>16) または(sdk<=16) を介して、RootView の追加または削除のすべてのタイミングを取得します。WindowManagerImpl
/**
 * RootView泄漏监听,主要利用了Curtains库实现对
 */
class RootViewWatcher(
  private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {

  private val listener = OnRootViewAddedListener { rootView ->
    // 判断rootView的窗口类型
    // 是否需要使用RootViewWatcher监听
    val trackDetached = when(rootView.windowType) {
      PHONE_WINDOW -> {
        when (rootView.phoneWindow?.callback?.wrappedCallback) {
          // Activities are already tracked by ActivityWatcher
          // 由于Activity已经有ActivityWatcher监听,这里直接返回false,即无需通过RootViewWatcher监听
          is Activity -> false
          is Dialog -> {
            // Use app context resources to avoid NotFoundException
            // https://github.com/square/leakcanary/issues/2137
            // 通过配置开启,默认不开启
            val resources = rootView.context.applicationContext.resources
            resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
          }
          // Probably a DreamService
          // 屏保等
          else -> true
        }
      }
      // Android widgets keep detached popup window instances around.
      POPUP_WINDOW -> false
      // Tooltip、Toast等进行监听
      TOOLTIP, TOAST, UNKNOWN -> true
    }
    if (trackDetached) {
      // 注册监听事件
      rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {

        val watchDetachedView = Runnable {
          // 接收发送的消息,使用ObjectWatcher对rootView进行监听
          reachabilityWatcher.expectWeaklyReachable(
            rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback"
          )
        }

        override fun onViewAttachedToWindow(v: View) {
          // 添加时移除消息
          mainHandler.removeCallbacks(watchDetachedView)
        }

        override fun onViewDetachedFromWindow(v: View) {
          // 监听RootView的移除事件,使用Handler发送消息处理
          mainHandler.post(watchDetachedView)
        }
      })
    }
  }

  override fun install() {
    // 注册RootView监听
    Curtains.onRootViewsChangedListeners += listener
  }

  override fun uninstall() {
    Curtains.onRootViewsChangedListeners -= listener
  }
}

  • これは主に、モニタリングメソッドを呼び出し、RootView の削除イベントをリッスンし、削除イベントでのモニタリングのために RootView を ObjectWatcher に渡すことDialog(有监听开关配置)、DreamService、Tooltip、Toastにより、他のタイプの RootView をリッスンするためのものです。rootView.addOnAttachStateChangeListeneronViewDetachedFromWindow

2.2.4 サービスウォッチャー

  • ServiceWatcher はサービスのメモリ リークを処理するためのリスナーであり、システムはグローバルなサービス削除の監視を提供しないため、LeakCanary もフックを通じて実装されます。
@SuppressLint("PrivateApi")
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {

  private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()

  private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }

  private val activityThreadInstance by lazy {
    activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!
  }

  private val activityThreadServices by lazy {
    val mServicesField =
      activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }

    @Suppress("UNCHECKED_CAST")
    mServicesField[activityThreadInstance] as Map<IBinder, Service>
  }

  private var uninstallActivityThreadHandlerCallback: (() -> Unit)? = null
  private var uninstallActivityManager: (() -> Unit)? = null

  override fun install() {
    checkMainThread()
    check(uninstallActivityThreadHandlerCallback == null) {
      "ServiceWatcher already installed"
    }
    check(uninstallActivityManager == null) {
      "ServiceWatcher already installed"
    }
    try {
      // Hook ActivityThread类中的mH.mCallback
      swapActivityThreadHandlerCallback { mCallback ->
        uninstallActivityThreadHandlerCallback = {
          swapActivityThreadHandlerCallback {
            mCallback
          }
        }
        Handler.Callback { msg ->
          if (msg.obj !is IBinder) {
            return@Callback false
          }

          // 监听Service.onStop()事件消息
          if (msg.what == STOP_SERVICE) {
            val key = msg.obj as IBinder
            // activityThreadServices是通过反射获取的ActivityThread类中的mServices成员变量<IBinder, Service>
            activityThreadServices[key]?.let {
              // 服务销毁前的处理,这里主要是暂存
              onServicePreDestroy(key, it)
            }
          }
          // Hook后继续执行Framework本身的逻辑
          mCallback?.handleMessage(msg) ?: false
        }
      }

      // Hook AMS IActivityManager
      swapActivityManager { activityManagerInterface, activityManagerInstance ->
        uninstallActivityManager = {
          swapActivityManager { _, _ ->
            activityManagerInstance
          }
        }
        Proxy.newProxyInstance(
          activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
        ) { _, method, args ->
          // 代理serviceDoneExecuting()方法
          if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
            val token = args!![0] as IBinder
            if (servicesToBeDestroyed.containsKey(token)) {
              // 处理Service销毁,主要是将service交给ObjectWatcher进行监控
              onServiceDestroyed(token)
            }
          }
          // 继续执行serviceDoneExecuting()本身的方法
          try {
            if (args == null) {
              method.invoke(activityManagerInstance)
            } else {
              method.invoke(activityManagerInstance, *args)
            }
          } catch (invocationException: InvocationTargetException) {
            throw invocationException.targetException
          }
        }
      }
    } catch (ignored: Throwable) {
      SharkLog.d(ignored) { "Could not watch destroyed services" }
    }
  }

  override fun uninstall() {
    checkMainThread()
    uninstallActivityManager?.invoke()
    uninstallActivityThreadHandlerCallback?.invoke()
    uninstallActivityManager = null
    uninstallActivityThreadHandlerCallback = null
  }

  private fun onServicePreDestroy(
    token: IBinder,
    service: Service
  ) {
    servicesToBeDestroyed[token] = WeakReference(service)
  }

  private fun onServiceDestroyed(token: IBinder) {
    servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
      serviceWeakReference.get()?.let { service ->
        reachabilityWatcher.expectWeaklyReachable(
          service, "${service::class.java.name} received Service#onDestroy() callback"
        )
      }
    }
  }

  /**
   * Hook修改ActivityThread类中的mH.mCallback
   * swap 是一个 lambda 表达式,参数为原对象,返回值为注入的新对象
   */
  private fun swapActivityThreadHandlerCallback(swap: (Handler.Callback?) -> Handler.Callback?) {
    val mHField =
      activityThreadClass.getDeclaredField("mH").apply { isAccessible = true }
    val mH = mHField[activityThreadInstance] as Handler

    val mCallbackField =
      Handler::class.java.getDeclaredField("mCallback").apply { isAccessible = true }
    val mCallback = mCallbackField[mH] as Handler.Callback?
    mCallbackField[mH] = swap(mCallback)
  }

  /**
   * Hook修改ActivityThread类中的mH.mCallback
   * swap 是一个 lambda 表达式,参数为 IActivityManager 的 Class 对象和接口原实现对象,返回值为注入的新对象
   */
  @SuppressLint("PrivateApi")
  private fun swapActivityManager(swap: (Class<*>, Any) -> Any) {
    val singletonClass = Class.forName("android.util.Singleton")
    val mInstanceField =
      singletonClass.getDeclaredField("mInstance").apply { isAccessible = true }

    val singletonGetMethod = singletonClass.getDeclaredMethod("get")

    val (className, fieldName) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      "android.app.ActivityManager" to "IActivityManagerSingleton"
    } else {
      "android.app.ActivityManagerNative" to "gDefault"
    }

    val activityManagerClass = Class.forName(className)
    val activityManagerSingletonField =
      activityManagerClass.getDeclaredField(fieldName).apply { isAccessible = true }
    val activityManagerSingletonInstance = activityManagerSingletonField[activityManagerClass]

    val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)

    val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
    // 将swap的返回值作为新对象,实现 Hook
    mInstanceField[activityManagerSingletonInstance] =
      swap(iActivityManagerInterface, activityManagerInstance!!)
  }

  companion object {
    private const val STOP_SERVICE = 116

    private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"
  }
}

  • フックの一般的な手順は次のとおりです。
    • メイン スレッド メッセージ ループのコールバックをフックしmH.mCallback、その中のメッセージをリッスンしSTOP_SERVICE、破棄する Service オブジェクトを一時的に保存します (ActivityThread.H には DESTROY_SERVICE メッセージがないため、onDestroy() イベントを直接監視できません。次の手順が必要です);
    • 動的プロキシ Hook AMS を使用して App IActivityManagerBinder オブジェクトと通信し、そのオブジェクト内のメソッドをプロキシしserviceDoneExecuting()、それを Service.onDestroy() の実行時間とみなし、一時的な Service オブジェクトを取得して、監視のために ObjectWatcher に渡します。

2.3 ObjectWatcher はメモリ リークをどのように監視するか

2.3.1 4 つの主要な Java リファレンスに関する知識

  • 強力な参照

    • 最も一般的な参照は、オブジェクトに強参照があり、null になっていない場合、OOM になっても仮想マシンはそのオブジェクトをリサイクルしません。
  • ソフトリファレンス

    • メモリが十分な場合、仮想マシンはメモリをリサイクルしませんが、メモリが不足すると自動的にリサイクルされます。イメージ キャッシュによく使用されます。
    SoftReference<String> softReference = new SoftReference<>(str);
    
    
  • 弱引用

    • メモリが十分であるかどうかに関係なく、ガベージ コレクション System.gc() を手動で呼び出すか、仮想マシンが自動的に GC するのを待っている限り、弱参照はリサイクルされます。主に Android のハンドラーなど、メモリリークが発生しやすい場所に配置するために使用されます。
    WeakReference<String> weakReference = new WeakReference<>(str);
    
    
  • ファントムリファレンス

    • 仮想参照はオブジェクトのライフサイクルを決定しません。オブジェクトがファントム参照のみを保持している場合、そのオブジェクトは参照がないかのようにガベージ コレクターによっていつでも回収される可能性があります。主に、PhantomReference を使用して作成され、ガベージ コレクターによってリサイクルされるオブジェクトのアクティビティを追跡するために使用されます。
  • 参照キュー ReferenceQueue

    • GC がオブジェクトをリサイクルしようとするときに、そのオブジェクトを指すソフト参照 (または弱参照、またはファントム参照) しかまだないことが判明した場合、GC はそのソフト参照 (または弱参照、またはファントム参照) をオブジェクトに追加する前にオブジェクトに追加します。オブジェクトをそれに関連付けられた参照キュー (ReferenceQueue) にリサイクルします。ソフト参照 (または弱い参照、またはファントム参照) オブジェクト自体が参照キュー内にある場合、それは参照オブジェクトが指すオブジェクトがリサイクルされることを意味します。
// 构造一个强引用  
Object obj = new Object();  
// 创建引用队列
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();  
// 利用强引用和引用队列构造弱引用  
WeakReference<Object> weakReference = new WeakReference<>(obj, referenceQueue);  
System.out.println("调用GC前弱引用:" + weakReference.get()); // java.lang.Object@6108b2d7  
System.out.println("调用GC前引用队列:" + referenceQueue.poll()); // null  
  
// 将强引用手动置null  
obj = null;  
// 手动GC或虚拟机自动GC都会回收弱引用,这里手动调用GC  
System.gc();  
  
System.out.println("调用GC后弱引用:" + weakReference.get()); // null  
System.out.println("调用GC后引用队列:" + referenceQueue.poll()); // java.lang.Object@6108b2d7

2.3.2 ObjectWatcher オブジェクトの監視

class ObjectWatcher constructor(
    private val clock: Clock,
    private val checkRetainedExecutor: Executor,
    private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {

    private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>()

    /** 被监控对象的映射表 */
    private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()

    /** 弱引用引用队列,与KeyedWeakReference相关联,对象正常销毁会存在这里面 */
    private val queue = ReferenceQueue<Any>()

    ...
    /** 监控对象泄漏 */
    @Synchronized
    override fun expectWeaklyReachable(
        watchedObject: Any,
        description: String
    ) {
        if (!isEnabled()) {
            return
        }
        // 移除watchedObjects中未泄漏的对象
        removeWeaklyReachableObjects()
        // 构造KeyedWeakReference 引用对象
        val key = UUID.randomUUID()
            .toString()
        val watchUptimeMillis = clock.uptimeMillis()
        val reference =
            KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
        SharkLog.d {
            "Watching " +
                    (if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") +
                    (if (description.isNotEmpty()) " ($description)" else "") +
                    " with key $key"
        }

        watchedObjects[key] = reference
        // 默认5秒后执行检查
        checkRetainedExecutor.execute {
            moveToRetained(key)
        }
    }
    ...

    @Synchronized
    private fun moveToRetained(key: String) {
        removeWeaklyReachableObjects()
        // 移除watchedObjects中未泄露的对象后剩余的判定为发生泄漏
        val retainedRef = watchedObjects[key]
        if (retainedRef != null) {
            retainedRef.retainedUptimeMillis = clock.uptimeMillis()
            // 回调通知LeakCanary内部处理
            onObjectRetainedListeners.forEach { it.onObjectRetained() }
        }
    }

    /** 移除队列中未泄漏的对象 */
    private fun removeWeaklyReachableObjects() {
        var ref: KeyedWeakReference?
        do {
            ref = queue.poll() as KeyedWeakReference?
            if (ref != null) {
                watchedObjects.remove(ref.key)
            }
        } while (ref != null)
    }
}

/** 弱引用包装类 */
class KeyedWeakReference(
  /** 被监控对象 */
  referent: Any,
  /** 映射表的Key */
  val key: String,
  /** 描述 */
  val description: String,
  /** 监控开始时间(引用创建时间) */
  val watchUptimeMillis: Long,
  /** 关联的引用队列 */
  referenceQueue: ReferenceQueue<Any>
) : WeakReference<Any>(
  referent, referenceQueue
) {
  /** 判定对象为泄漏对象的时间,-1表示非泄漏对象或还未判定完毕 */
  @Volatile
  var retainedUptimeMillis = -1L

  override fun clear() {
    super.clear()
    retainedUptimeMillis = -1L
  }

  companion object {
    /** 记录最近一次触发Heap Dump的时间 */
    @Volatile
    @JvmStatic var heapDumpUptimeMillis = 0L
  }
}

  • 主に3つのステップを経ました
  1. 監視対象オブジェクトの弱い参照watchedObjectを作成しKeyedWeakReference、それを <UUID, KeyedWeakReference> のマッピング テーブルに保存します。
  2. PostDelay は、5 秒後に参照オブジェクトが参照キューに表示されるかどうかを確認し、キューに表示されていれば、監視オブジェクトがリークしていないことを意味します。その後、マッピング テーブル内のリークされていないレコードが削除され、retainedUptimeMillisリークされた参照オブジェクトのフィールドが更新されて、リーク済みとしてマークされます。
  3. onObjectRetained新しいメモリ リークがコールバックを通じて内部で発生したことを LeakCanary に通知する

2.4 ヒープをダンプしてメモリ スナップショット ファイルを取得する

  • ObjectWatcher は、監視対象オブジェクトがリークしたと判断すると、インターフェイス メソッドを通じてOnObjectRetainedListener.onObjectRetained()LeakCanary の内部マネージャーにコールバックします。InternalLeakCanary
  • 分析作業には時間がかかるため、LeakCanary はメモリ リーク オブジェクトを見つけるたびに分析作業を実行するのではなく、次の 2 つのインターセプトを実行します。
  • 1. 漏洩オブジェクトの数がしきい値に達していない、またはバックグラウンドに入る時間がしきい値に達していない。
  • 2. 最後の HeapDump からの計算距離が 60 秒を超えていない。
// InternalLeakCanary.kt
override fun onObjectRetained() = scheduleRetainedObjectCheck()

fun scheduleRetainedObjectCheck() {
  if (this::heapDumpTrigger.isInitialized) {
    heapDumpTrigger.scheduleRetainedObjectCheck()
  }
}

// HeapDumpTrigger.kt
fun scheduleRetainedObjectCheck(
  delayMillis: Long = 0L
) {
  val checkCurrentlyScheduledAt = checkScheduledAt
  // 避免重复postDelayed
  if (checkCurrentlyScheduledAt > 0) {
    return
  }
  checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
  backgroundHandler.postDelayed({
    checkScheduledAt = 0
    checkRetainedObjects()
  }, delayMillis)
}

private fun checkRetainedObjects() {
  val iCanHasHeap = HeapDumpControl.iCanHasHeap()

  val config = configProvider()

  if (iCanHasHeap is Nope) {
    if (iCanHasHeap is NotifyingNope) {
      // Before notifying that we can't dump heap, let's check if we still have retained object.
      // 泄漏计数
      var retainedReferenceCount = objectWatcher.retainedObjectCount
      // 泄漏计数>0时主动调用GC
      if (retainedReferenceCount > 0) {
        // 这个方法是调用RunTime.getRuntime().GC()并休眠等待100ms
        gcTrigger.runGc()
        // GC后再获取泄漏计数
        retainedReferenceCount = objectWatcher.retainedObjectCount
      }

      val nopeReason = iCanHasHeap.reason()
      // 这里会判断泄漏计数是否>5(默认阈值)
      val wouldDump = !checkRetainedCount(
        retainedReferenceCount, config.retainedVisibleThreshold, nopeReason
      )

      if (wouldDump) {
        val uppercaseReason = nopeReason[0].toUpperCase() + nopeReason.substring(1)
        // 回调onEvent
        onRetainInstanceListener.onEvent(DumpingDisabled(uppercaseReason))
        // 展示通知
        showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = uppercaseReason
        )
      }
    } else {
      SharkLog.d {
        application.getString(
          R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
        )
      }
    }
    return
  }

  var retainedReferenceCount = objectWatcher.retainedObjectCount

  if (retainedReferenceCount > 0) {
    gcTrigger.runGc()
    retainedReferenceCount = objectWatcher.retainedObjectCount
  }

  if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

  val now = SystemClock.uptimeMillis()
  val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
  // 计算距离上一次HeapDump时间未超过60s会拦截
  if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
    onRetainInstanceListener.onEvent(DumpHappenedRecently)
    showRetainedCountNotification(
      objectCount = retainedReferenceCount,
      contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
    )
    scheduleRetainedObjectCheck(
      delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
    )
    return
  }

  // 移除通知
  dismissRetainedCountNotification()
  val visibility = if (applicationVisible) "visible" else "not visible"
  // 触发dumpHeap分析
  dumpHeap(
    retainedReferenceCount = retainedReferenceCount,
    retry = true,
    reason = "$retainedReferenceCount retained objects, app is $visibility"
  )
}

2.5 ヒープスナップショットの分析 dumpHeap

  • LeakCanary は.hprofヒープ スナップショット ファイルを生成し、LeakCanary 内部イベントを送信しましたHeapDumpLeakCanary の設定項目には複数のイベント コンシューマー EventListener が設定されており、これら 3 つはアプリの現在の依存関係に応じて最適な実行戦略を選択します。
  • 1 - WorkerManager のマルチプロセス分析
  • 2 - WorkManager の非同期分析
  • 3 - 非同期スレッド分析 (隠蔽戦略)
// LeakCanary.kt
val eventListeners: List<EventListener> = listOf(
  LogcatEventListener,
  ToastEventListener,
  LazyForwardingEventListener {
    if (InternalLeakCanary.formFactor == TV) TvEventListener else NotificationEventListener
  },
  when {
     // WorkManager多进程分析
      RemoteWorkManagerHeapAnalyzer.remoteLeakCanaryServiceInClasspath ->
        RemoteWorkManagerHeapAnalyzer
      // WorkManager 异步分析
      WorkManagerHeapAnalyzer.validWorkManagerInClasspath -> WorkManagerHeapAnalyzer
      // 异步线程分析(兜底策略)
      else -> BackgroundThreadHeapAnalyzer
  }
),

2.5.1 WorkerManager のマルチプロセス分析

object RemoteWorkManagerHeapAnalyzer : EventListener {

  private const val REMOTE_SERVICE_CLASS_NAME = "leakcanary.internal.RemoteLeakCanaryWorkerService"

  // 这里通过RemoteLeakCanaryWorkerService这个类是否加载成功来判断
  // 是否有'com.squareup.leakcanary:leakcanary-android-process:2.9.1'这个依赖
  internal val remoteLeakCanaryServiceInClasspath by lazy {
    try {
      Class.forName(REMOTE_SERVICE_CLASS_NAME)
      true
    } catch (ignored: Throwable) {
      false
    }
  }

  override fun onEvent(event: Event) {
    if (event is HeapDump) {
      val application = InternalLeakCanary.application
      // 创建并分发 WorkManager 多进程请求
      val heapAnalysisRequest =
        OneTimeWorkRequest.Builder(RemoteHeapAnalyzerWorker::class.java).apply {
          val dataBuilder = Data.Builder()
            .putString(ARGUMENT_PACKAGE_NAME, application.packageName)
            .putString(ARGUMENT_CLASS_NAME, REMOTE_SERVICE_CLASS_NAME)
          setInputData(event.asWorkerInputData(dataBuilder))
          with(WorkManagerHeapAnalyzer) {
            addExpeditedFlag()
          }
        }.build()
      SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
      val workManager = WorkManager.getInstance(application)
      workManager.enqueue(heapAnalysisRequest)
    }
  }
}

internal class RemoteHeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) :
  RemoteListenableWorker(appContext, workerParams) {

  override fun startRemoteWork(): ListenableFuture<Result> {
    val heapDump = inputData.asEvent<HeapDump>()
    val result = SettableFuture.create<Result>()
    heapAnalyzerThreadHandler.post {
      // 分析堆快照
      val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(heapDump, isCanceled = {
        result.isCancelled
      }) { progressEvent ->
        if (!result.isCancelled) {
          // 发送分析进度事件
          InternalLeakCanary.sendEvent(progressEvent)
        }
      }
      if (result.isCancelled) {
        SharkLog.d { "Remote heap analysis for ${heapDump.file} was canceled" }
      } else {
        // 发送分析完成事件
        InternalLeakCanary.sendEvent(doneEvent)
        result.set(Result.success())
      }
    }
    return result
  }

  override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
    return applicationContext.heapAnalysisForegroundInfoAsync()
  }
}

2.5.2 WorkManager の非同期分析

object WorkManagerHeapAnalyzer : EventListener {

  // 判断是否包含WorkManager
  internal val validWorkManagerInClasspath by lazy {
    try {
      Class.forName("androidx.work.WorkManager")
      val dataBuilderClass = Class.forName("androidx.work.Data$Builder")
      dataBuilderClass.declaredMethods.any { it.name == "putByteArray" }.apply {
        if (!this) {
          SharkLog.d { "Could not find androidx.work.Data$Builder.putByteArray, WorkManager should be at least 2.1.0." }
        }
      }
    } catch (ignored: Throwable) {
      false
    }
  }

  // setExpedited() requires WorkManager 2.7.0+
  private val workManagerSupportsExpeditedRequests by lazy {
    try {
      Class.forName("androidx.work.OutOfQuotaPolicy")
      true
    } catch (ignored: Throwable) {
      false
    }
  }

  internal fun OneTimeWorkRequest.Builder.addExpeditedFlag() = apply {
    if (workManagerSupportsExpeditedRequests) {
      setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    }
  }

  override fun onEvent(event: Event) {
    if (event is HeapDump) {
      val heapAnalysisRequest = OneTimeWorkRequest.Builder(HeapAnalyzerWorker::class.java).apply {
        setInputData(event.asWorkerInputData())
        addExpeditedFlag()
      }.build()
      SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
      val application = InternalLeakCanary.application
      WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
    }
  }
}

internal class HeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) :
  Worker(appContext, workerParams) {
  override fun doWork(): Result {
    // 分析堆快照
    val doneEvent =
      AndroidDebugHeapAnalyzer.runAnalysisBlocking(inputData.asEvent()) { event ->
        // 发送分析进度事件
        InternalLeakCanary.sendEvent(event)
      }
    // 发送分析完成事件
    InternalLeakCanary.sendEvent(doneEvent)
    return Result.success()
  }
 ...
}

2.5.3 一般的な非同期スレッド解析BackgroundThreadHeapAnalyzer

object BackgroundThreadHeapAnalyzer : EventListener {

  internal val heapAnalyzerThreadHandler by lazy {
    val handlerThread = HandlerThread("HeapAnalyzer")
    handlerThread.start()
    Handler(handlerThread.looper)
  }

  override fun onEvent(event: Event) {
    if (event is HeapDump) {
      heapAnalyzerThreadHandler.post {
        // 分析堆快照
        val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(event) { event ->
          // 发送分析进度事件
          InternalLeakCanary.sendEvent(event)
        }
        // 发送分析完成事件
        InternalLeakCanary.sendEvent(doneEvent)
      }
    }
  }
}

  • 3 つの方法で、LeakCanary は最終的にサブスレッドまたはサブプロセスの実行メソッドを通じてAndroidDebugHeapAnalyzer.runAnalysisBlockingヒープ スナップショットを分析し、分析プロセス中および分析完了後にコールバック イベントを送信することがわかります。
  • ヒープ スナップショット分析は最終的に Shark の HeapAnalizer に引き渡され、ここでは分析は実行されません。中心的なプロセスは次のとおりです。
  • 1. ヒープ スナップショットでリークされたオブジェクトを検索します。デフォルトでは、KeyedWeakReference タイプのオブジェクトが検索されます。
  • 2. KeyedWeakReference オブジェクトの最短の参照チェーンを分析し、参照チェーンの署名に従ってグループ化し、アプリケーション リークとライブラリ リークに従って分類します。
  • 3. 解析完了イベントを返します。

3. まとめ

  1. ContentProvider を使用して初期化する
  2. 5 つの Android 漏洩シナリオの監視に登録する
    • アクティビティは、application.registerActivityLifecycleCallbacks (ActivityLifecycleCallbacks コールバック) を使用して、グローバルなアクティビティ ライフ サイクルを監視します。
    • Fragment は、まずApplication.registerActivityLifecycleCallbacks(…)インターフェイスを通じて Activity.onCreate() イベントをリッスンし、次にFragmentManager.registerFragmentLifecycleCallbacks(…)インターフェイスを通じて Fragment のライフ サイクルをリッスンします。
    • ViewModel はHookViewModelStore を通じて実装されます
    • サービスは、フックmH.mCallbackコールバックと監視メッセージを通じてSTOP_SERVICE実装されます。
    • RootView は、Curtains ライブラリを使用して WMS サービスをフックすることによって内部的にWindowManagerGlobal実装されます。
  3. 破棄コールバックを受信した後、リサイクルするオブジェクトに基づいて KeyedWeakReference を作成し、ReferenceQueue に関連付けます。5 秒待って、関連するオブジェクトがリサイクルされているかどうかを確認します。リサイクルされていない場合は、サービスを開始し、ヒープをダンプしてメモリを取得しますスナップショット ファイル.hprof
  4. Shark ライブラリを通じてファイルを解析し.hprof、リークされたオブジェクトを取得し、リークされたオブジェクトから GC ルートまでの最短パスを計算します。
  5. 複数のリークパスを結合して解析結果を出力し、結果をビジュアルインターフェイスに表示します

おすすめ

転載: blog.csdn.net/qq_25462179/article/details/132760099