Breve introducción y uso de LeakCanary

1. Introducción:

LeakCanray es la herramienta de análisis de fugas de memoria Java de código abierto de Square, que se utiliza para detectar fugas de memoria comunes en aplicaciones de Android durante la fase de desarrollo.

  • Admite la supervisión de pérdidas de memoria en los siguientes cinco escenarios de Android:
    1. Objeto de actividad destruido (entra en estado DESTRUIDO)
    2. Objetos Fragmento destruidos y objetos Vista Fragmento (entraron en estado DESTRUIDO)
    3. Objeto ViewModel borrado (entra en estado BORRADO)
    4. Objeto de servicio destruido (entra en estado DESTRUIDO)
    5. Objeto RootView eliminado de WindowManager

1.2 Uso

  • Simplemente agregue la dependencia de LeakCanary en build.gradle (LeakCanary usa ContentProvider de forma predeterminada para lograr una inicialización no intrusiva). Nota: LeakCanary es una herramienta que solo se usa en el entorno de desarrollo, así que recuerde usarla debugImplementationpara configurar dependencias.
// 普通ContentProvider方式
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'

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

2. Análisis del código fuente

2.1 Inicialización

  • LeakCanary en la era 1.x requería agregar código de inicialización a Application.onCreate(). Después de 2.x, LeakCanary usó el ContentProvidermecanismo de inicio para llamar indirectamente a la API de inicialización, logrando una inicialización de LeakCanary no invasiva.
  • La ubicación específica está en el submódulo fugascanary-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
  }
  ...
}

 El código de inicialización real esAppWatcher.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)
  )
}

 Se divide en cuatro categorías de monitoreo para monitorear Actividad, Fragmento, ViewModel, RootView y Servicio respectivamente. A continuación, analicemos cómo se monitorean específicamente estas cuatro categorías.

2.2 Cuatro categorías principales de monitoreo realizan el monitoreo de cinco escenarios de fugas

2.2.1 Vigilante de actividad

  • ActivityWatcher realiza principalmente el seguimiento de la actividad
/** 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)
  }
}

  • El monitoreo de actividad es relativamente simple: puede usar directamente la API de monitoreo global proporcionada application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)y luego entregar la actividad a objectWatcher para su análisis en onDestroy de cada actividad.

2.2.2 FragmentAndViewModelWatcher

  • FragmentAndViewModelWatcher implementa principalmente el monitoreo de Fragment, Fragment view y 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)
    }
    ...
}

  • El monitoreo de Fragment y Fragment View se implementa principalmente FragmentAndViewModelWatchera través de: Primero, Application.registerActivityLifecycleCallbacks(…)monitorea el evento Activity.onCreate () a través de la interfaz y luego FragmentManager.registerFragmentLifecycleCallbacks(…)monitorea el ciclo de vida de Fragment a través de la interfaz.
  • Hay tres estados según la versión de Android y el paquete Fragment, a saber AndroidOFragmentDestroyWatcher,,, Aquí tomamos AndroidXFragmentDestroyWatcher como ejemplo.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)
    }
  }
}

  • Esto también incluye la lógica de monitoreo de ViewModel, que se usa en Activity.onCreate() y Fragment.onCreate() ViewModelClearedWatcherpara el monitoreo. Dado que el sistema no proporciona un método para el monitoreo global de ViewModel, ViewModelClearedWatcher se Hookimplementa a través de métodos. Tomemos un mirar.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)
    }
  }
}

  • Se puede ver que el monitoreo de ViewModel consiste en crear un ViewModel personalizado (es decir, ViewModelClearedWatcher) en Activity.onCreate()/Fragment.onCreate(), luego escuchar onCleared() de ViewModelClearedWatcher y obtener la respuesta correspondiente a través de Hook ViewModelStore. Todos los objetos ViewModel en el dominio (Actividad/Fragmento) y finalmente entregados a ObjectWatcher para su monitoreo.

2.2.3 Vigilante RootView

  • RootViewWatcher es un oyente que maneja las pérdidas de memoria de RootView. Dado que el sistema no proporciona monitoreo global de RootView, LeakCannary aún se maneja a través de Hook. Sin embargo, en lugar de engancharse a sí mismo, se usa Curtains, otra biblioteca de código abierto de Square, para manejarlo. principalmente a través del servicio Hook WMS interno WindowManagerGlobal(sdk>16) o WindowManagerImpl(sdk<=16) para obtener el momento de agregar o eliminar todos los RootViews
/**
 * 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
  }
}

  • Principalmente para Dialog(有监听开关配置)、DreamService、Tooltip、Toastmonitorear otros tipos de RootView, llamando al método rootView.addOnAttachStateChangeListenerde monitoreo onViewDetachedFromWindow, monitoreando el evento de eliminación de RootView y entregando RootView a ObjectWatcher para monitorear el evento de eliminación.

2.2.4 Vigilante del servicio

  • ServiceWatcher es un oyente que maneja las pérdidas de memoria del Servicio. El sistema no proporciona monitoreo global de eliminación del Servicio, por lo que LeaakCanary también se implementa a través de Hook.
@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"
  }
}

  • Los pasos generales de Hook son:
    • Enganche la devolución de llamada del bucle de mensajes del hilo principal mH.mCallback, monitoree STOP_SERVICElos mensajes y almacene temporalmente el objeto de Servicio que está a punto de ser destruido (dado que no hay ningún mensaje DESTROY_SERVICE en ActivityThread.H, el evento onDestroy() no se puede monitorear directamente y el se requieren los siguientes pasos);
    • Utilice el proxy dinámico Hook AMS para comunicarse con el objeto Binder de la aplicación IActivityManagery proxy los métodos que contiene serviceDoneExecuting(). Esto se considera el tiempo de ejecución de Service.onDestroy (), y el objeto de servicio temporal se obtiene y se entrega a ObjectWatcher para supervisión.

2.3 Cómo ObjectWatcher monitorea las pérdidas de memoria

2.3.1 Los cuatro conocimientos relacionados con referencias principales de Java

  • fuerte referencia

    • La referencia más común es que cuando el objeto tiene una referencia fuerte y no está anulado, la máquina virtual no lo reciclará incluso si pasa a OOM.
  • referencia suave

    • Cuando la memoria es suficiente, la máquina virtual no la reciclará. Cuando la memoria es insuficiente, la reciclará automáticamente. A menudo se utiliza para el almacenamiento en caché de imágenes.
    SoftReference<String> softReference = new SoftReference<>(str);
    
    
  • cita débil

    • Independientemente de si la memoria es suficiente, siempre que llame manualmente a la recolección de basura System.gc() o espere a que la máquina virtual realice GC automáticamente, las referencias débiles se reciclarán. Se utiliza principalmente para colocar en ubicaciones propensas a pérdidas de memoria, como Handler en Android.
    WeakReference<String> weakReference = new WeakReference<>(str);
    
    
  • referencia virtual

    • Las referencias virtuales no determinan el ciclo de vida del objeto. Si un objeto contiene solo referencias fantasmas, el recolector de basura puede reclamarlo en cualquier momento como si no tuviera referencias. Se utiliza principalmente para rastrear la actividad de los objetos reciclados por el recolector de basura, creados con PhantomReference.
  • Cola de referenciaCola de referencia

    • Cuando gc está listo para reciclar un objeto, si descubre que solo tiene una referencia suave (o referencia débil o referencia virtual) apuntándolo, agregará esta referencia suave (o referencia débil o referencia virtual) antes de reciclar el objeto. objeto a la cola de referencia (ReferenceQueue) asociada con él. Si un objeto de referencia suave (o referencia débil o referencia virtual) está en la cola de referencia, significa que el objeto al que apunta el objeto de referencia se ha reciclado;
// 构造一个强引用  
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 Monitoreo de objetos 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
  }
}

  • Pasó principalmente por tres pasos.
  1. watchedObjectCree una referencia débil para el objeto monitoreado KeyedWeakReferencey guárdela en la tabla de mapeo de <UUID, KeyedWeakReference>;
  2. PostDelay verifica si el objeto de referencia aparece en la cola de referencia después de cinco segundos. Si aparece en la cola, significa que el objeto monitoreado no se ha filtrado. Posteriormente, se eliminan los registros no filtrados en la tabla de mapeo y retainedUptimeMillislos campos de los objetos de referencia filtrados se actualizan para marcarlos como filtrados.
  3. onObjectRetainedNotificar a LeakCanary que se ha producido una nueva pérdida de memoria internamente mediante una devolución de llamada

2.4 Volcar el montón para obtener un archivo de instantánea de memoria

  • Después de que ObjectWatcher determine que el objeto monitoreado se ha filtrado, volverá a OnObjectRetainedListener.onObjectRetained()llamar al administrador interno de LeakCanary a través del método de interfaz.InternalLeakCanary
  • Dado que el trabajo de análisis lleva mucho tiempo, LeakCanary, naturalmente, no realizará el trabajo de análisis cada vez que encuentre un objeto de pérdida de memoria, sino que realizará dos intercepciones:
  • 1. El recuento de objetos filtrados no alcanza el umbral, o el tiempo para entrar en segundo plano no alcanza el umbral.
  • 2. La distancia de cálculo desde el último HeapDump no ha excedido los 60 segundos.
// 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 Analizar el volcado de instantáneas del montón

  • LeakCanary generó con éxito .hprofun archivo de instantánea del montón y envió un evento interno de LeakCanary HeapDump. En los elementos de configuración de LeakCanary se configuran múltiples consumidores de eventos, EventListener. Estos tres seleccionarán la estrategia de ejecución óptima en función de las dependencias actuales de la aplicación:
  • 1 - Análisis multiproceso de WorkerManager
  • 2 - Análisis asincrónico de WorkManager
  • 3-Análisis de hilos asincrónicos (estrategia de encubrimiento)
// 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 Análisis multiproceso de 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 Análisis asincrónico de 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 Análisis de subprocesos asincrónicos comunes 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)
      }
    }
  }
}

  • AndroidDebugHeapAnalyzer.runAnalysisBlockingSe puede ver que entre los tres métodos, LeakCanary analiza la instantánea del montón a través del método de ejecución de subproceso o subproceso y envía eventos de devolución de llamada durante el proceso de análisis y después de que se completa el análisis.
  • El análisis de instantáneas del montón finalmente lo completa HeapAnalizer en Shark. El análisis no se realizará aquí. El proceso principal es:
  • 1. Busque objetos filtrados en la instantánea del montón. El valor predeterminado es buscar objetos de tipo KeyedWeakReference;
  • 2. Analice la cadena de referencia más corta del objeto KeyedWeakReference, agrúpela según la firma de la cadena de referencia y clasifíquela según fugas de aplicaciones y fugas de biblioteca;
  • 3. Devuelve el evento de finalización del análisis.

3. Resumen

  1. Inicializar con ContentProvider
  2. Monitoreo de registros para 5 escenarios de fugas de Android
    • La actividad utiliza application.registerActivityLifecycleCallbacks (devolución de llamada de ActivityLifecycleCallbacks) para monitorear el ciclo de vida global de la actividad
    • Fragment primero Application.registerActivityLifecycleCallbacks(…)escucha el evento Activity.onCreate () a través de la interfaz y luego FragmentManager.registerFragmentLifecycleCallbacks(…)escucha el ciclo de vida de Fragment a través de la interfaz.
    • ViewModel se Hookimplementa a través de ViewModelStore
    • El servicio se implementa a través de devoluciones de llamadas de Hook mH.mCallbacky mensajes de monitoreo .STOP_SERVICE
    • WindowManagerGlobalRootView se implementa internamente mediante el uso de la biblioteca Curtains para conectar el servicio WMS.
  3. Después de recibir la devolución de llamada de destrucción, cree una KeyedWeakReference basada en el objeto a reciclar y asóciela con ReferenceQueue. Espere 5 segundos para verificar si el objeto relevante se ha reciclado. De lo contrario, inicie el servicio y descargue el montón para obtener la memoria. archivo de instantánea .hprof.
  4. Analice el archivo a través de la biblioteca Shark .hprof, obtenga el objeto filtrado y calcule la ruta más corta desde el objeto filtrado hasta las raíces de GC.
  5. Combine múltiples rutas de fuga y resultados de análisis de salida, y muestre los resultados en la interfaz visual

Supongo que te gusta

Origin blog.csdn.net/qq_25462179/article/details/132760099
Recomendado
Clasificación