敞开成长之旅!这是我参与「日新计划 12 月更文应战」的第21天,点击查看活动详情

LeakCanary内部用到了Refercence及ReferenceQueue来完结对方针是否被收回的监听。这是LeakCanary的中心逻辑,因此在讲解LeakCanary之前,咱们先来简略了解一下Refercence及ReferenceQueue。

1、Refercence及ReferenceQueue

1.1 基本概念

Reference即引证,是一个泛型抽象类。Android中的SoftReference(软引证)、WeakReference(弱引证)、PhantomReference(虚引证)都是承继自Reference。来看下Reference的几个首要成员变量。

public abstract class Reference<T> {
    // 引证方针,被收回时置null
    volatile T referent;
    //保存行将被收回的reference方针
    final ReferenceQueue<? super T> queue;
    //在Enqueued状况下即引证参加行列时,指向下一个待处理Reference方针,默以为null
    Reference queueNext;
    //在Pending状况下,待入列引证,默以为null
    Reference<?> pendingNext;
}

Reference有四种状况:Active、Pending、Enqueued、Inactive。声明的时分默许Active状况。

ReferenceQueue则是一个单向链表完结的行列数据结构,存储的是Reference方针。包含了入列enqueue、出列poll和移除remove操作。

1.2 方针收回监听

Reference配合ReferenceQueue就能够完结方针收回监听了,先经过一个示例来看看是怎样完结的。

//创立一个引证行列
ReferenceQueue queue = new ReferenceQueue();
//创立弱引证,并相关引证行列queue
WeakReference reference = new WeakReference(new Object(),queue);
System.out.println(reference);
System.gc();
//当reference被成功收回后,能够从queue中获取到该引证
System.out.println(queue.remove());

示例中的方针当然是能够正常收回的,所以收回后能够在相关的引证行列queue中获取到该引证。反之,若某个应该被收回的方针,GC结束后在queue中未找到该引证,则表明该引证存在内存走漏危险,这也便是LeakCanary的基本原理了。

2、LeakCanary基本原理

为了更好的对LeakCanary源码进行分部解析,咱们先对LeakCanary完结内存走漏的整体进程做一个概括。后边在分部对整个流程的源码进行解析。

  1. 初始化。
  2. 增加相关监听方针毁掉监听。LeakCanary会默许监听Activity、Fragment、Fragment的View、ViewModel是否收回。
  3. 收到毁掉回调后,依据要收回方针创立KeyedWeakReference和ReferenceQueue,并相关。
  4. 延迟5秒查看相关方针是否被收回。
  5. 假如没有被收回就经过dump heap获取hprof文件。
  6. 经过Shark库解析hprof文件,获取走漏方针,被核算走漏方针到GC roots的最短途径。
  7. 兼并多个走漏途径并输出剖析成果。
  8. 将成果展现到可视化界面。

3、LeakCanary源码解析

在2.0之后的版本只需求在build.gradle引进项目就完事了

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'

3.1、进口

2.0之前的版本接入进程除了在build.gradle中引进项目外,还需求调用LeakCanary.install(this),来进行初始化工作。一般都是在Application的onCreate()办法中调用。

在2.0之后的版本只需求在build.gradle引进项目就完事了。那么问题来了:2.0之后的版本初始化工作是在哪里完结的呢?

找了良久,终于在项目工程:leakcanary-object-watcher-android的manifest文件中发现了隐秘:

<application>
  <provider
      android:name="leakcanary.internal.MainProcessAppWatcherInstaller"
      android:authorities="${applicationId}.leakcanary-installer"
      android:enabled="@bool/leak_canary_watcher_auto_install"
      android:exported="false"/>
</application>

这儿注册了一个承继自ContentProvider的MainProcessAppWatcherInstaller。咱们知道在app启动时,会先调用注册的ContentProvider的onCreate完结初始化,在MainProcessAppWatcherInstaller onCreate中果然找到了了解的install办法:

override fun onCreate(): Boolean {
  val application = context!!.applicationContext as Application
  AppWatcher.manualInstall(application)
  return true
}

调用链:AppWatcher.manualInstall–>InternalAppWatcher.install。具体的初始化逻辑是在InternalAppWatcher,来看源码:

@JvmOverloads
fun manualInstall(
  application: Application,
  retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
  watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
  //保证在主线程,不然抛出UnsupportedOperationException反常
  checkMainThread()
  if (isInstalled) {
    throw IllegalStateException(
      "AppWatcher already installed, see exception cause for prior install call", installCause
    )
  }
  check(retainedDelayMillis >= 0) {
    "retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
  }
  this.retainedDelayMillis = retainedDelayMillis
  if (application.isDebuggableBuild) {
    LogcatSharkLog.install()
  }
  // Requires AppWatcher.objectWatcher to be set
  // 初始化 InternalLeakCanary 内部引擎
  LeakCanaryDelegate.loadLeakCanary(application)
  // 注册五种 Android 走漏场景的监控 Hook 点
  watchersToInstall.forEach {
    it.install()
  }
  // Only install after we're fully done with init.
  installCause = RuntimeException("manualInstall() first called here")
}

ContentProvider的中心办法CURD在AppWatcherInstaller都是空完结,只用到了onCreate。需求留意的是ContentProvider.onCreate调用机遇介于Application的attachBaseContext和onCreate之间,所以不能依靠之后初始化的其他SDK。

ContentProvider 的常规用法是供给内容服务,而另一个特别的用法是供给无侵入的初始化机制,这在第三方库中很常见,Jetpack 中供给的轻量级初始化结构App Startup也是根据 ContentProvider 的计划。

LeakCanary源码分析(2.10最新版)

在初始进程中,对应四种场景的内存走漏监听

fun appDefaultWatchers(
  application: Application,
  reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
  return listOf(
    ActivityWatcher(application, reachabilityWatcher),
    FragmentAndViewModelWatcher(application, reachabilityWatcher),
    RootViewWatcher(reachabilityWatcher),
    ServiceWatcher(reachabilityWatcher)
  )
}

LeakCanary 的初始化工程能够概括为 2 项内容:

  • 1、初始化 LeakCanary 内部剖析引擎;
  • 2、在 Android Framework 上注册四种 Android 走漏场景的监控。

3.2、InternalLeakCanary 引擎初始化

进入源码

LeakCanaryDelegate.loadLeakCanary(application)

经过反射构建内部引擎方针

internal object LeakCanaryDelegate {
  @Suppress("UNCHECKED_CAST")
  val loadLeakCanary by lazy {
    try {
      val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
      leakCanaryListener.getDeclaredField("INSTANCE")
        .get(null) as (Application) -> Unit
    } catch (ignored: Throwable) {
      NoLeakCanary
    }
  }

依据kotlin特性会履行invoke办法

override fun invoke(application: Application) {
    _application = application
    // 1. 查看是否运行在 debug 构建变体,不然抛出反常
    checkRunningInDebuggableBuild()
    // 2. 注册走漏回调,在 ObjectWathcer 断定方针产生走漏会后回调 onObjectRetained() 办法
    AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
    // 3. 废物收回触发器(用于调用 Runtime.getRuntime().gc())
    val gcTrigger = GcTrigger.Default
    // 4. 装备供给器
    val configProvider = { LeakCanary.config }
    // 5. (主角) 创立 HeapDump 触发器
    heapDumpTrigger = HeapDumpTrigger(...)
    // 6. App 前后台切换监听
    application.registerVisibilityListener { applicationVisible ->
        this.applicationVisible = applicationVisible
        heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
    }
    // 7. 前台 Activity 监听(用于发送 Heap Dump 进行中的大局 Toast)
    registerResumedActivityListener(application)
    // 8. 增加可视化剖析陈述的桌面快捷进口
    addDynamicShortcut(application)
}
override fun onObjectRetained() = scheduleRetainedObjectCheck()
fun scheduleRetainedObjectCheck() {
    heapDumpTrigger.scheduleRetainedObjectCheck()
}

注册前后台监听

// App 前后台切换状况改变回调
fun onApplicationVisibilityChanged(applicationVisible: Boolean) {
    if (applicationVisible) {
        // App 可见
        applicationInvisibleAt = -1L
    } else {
        // App 不行见
        applicationInvisibleAt = SystemClock.uptimeMillis()
        scheduleRetainedObjectCheck(delayMillis = AppWatcher.retainedDelayMillis)
    } 
}
fun scheduleRetainedObjectCheck(delayMillis: Long = 0L) {
    // 已简化:源码此处运用时刻戳阻拦,防止重复 postDelayed
    backgroundHandler.postDelayed({
        checkScheduledAt = 0
        checkRetainedObjects()
    }, delayMillis)
}

总结:

创立 HeapDumpTrigger 触发器,并在 Android Framework 上注册前后台切换监听、前台 Activity 监听和 ObjectWatcher 的走漏监听。

3.2、注入对四种 Android 走漏场景的监控

完结在方针的运用生命周期结束后,自动将方针交给ObjectWatcher进行监控。

fun appDefaultWatchers(
  application: Application,
  reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
  return listOf(
    ActivityWatcher(application, reachabilityWatcher),
    FragmentAndViewModelWatcher(application, reachabilityWatcher),
    RootViewWatcher(reachabilityWatcher),
    ServiceWatcher(reachabilityWatcher)
  )
}

Activity 收回监听

ActivityWatcher类中 经过Application#registerActivityLifecycleCallbacks(…)接口监听 Activity#onDestroy 事情,将当时 Activity 方针交给 ObjectWatcher 监控;

companion object {
    fun install(
      application: Application,
      objectWatcher: ObjectWatcher,
      configProvider: () -> Config
    ) {
        //实例化ActivityDestroyWatcher
      val activityDestroyWatcher =
        ActivityDestroyWatcher(objectWatcher, configProvider)
        //注册ActivityLifecycle监听
      application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
    }
}

registerActivityLifecycleCallbacks是Android Application的一个办法,注册了该办法,能够经过回调获取app中每一个Activity的生命周期改变。再来看看ActivityDestroyWatcher对生命周期回调的处理:

private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        if (configProvider().watchActivities) {
        //经过objectWatcher监听改activity是否被毁掉收回
          objectWatcher.watch(
              activity, "${activity::class.java.name} received Activity#onDestroy() callback"
          )
        }
      }
}

ActivityLifecycleCallbacks生命周期回调有那么多,为什么只用重写其间一个?关键在于by noOpDelegate(),经过类托付机制将其他回调完结都交给noOpDelegate,而noOpDelegate是一个空完结的动态署理。新姿态get+1,在遇到只需求完结接口的部分办法时,就能够这么玩了,其他办法完结都托付给空完结署理类就好了。

Fragment 收回监听

FragmentAndViewModelWatcher中完结,首先是经过Application#registerActivityLifecycleCallbacks(…)接口监听 Activity#onCreate 事情,再经过FragmentManager#registerFragmentLifecycleCallbacks(…)接口监听 Fragment 的生命周期:

来看一下FragmentDestroyWatcher.install的完结:

fun install(
    application: Application,
    objectWatcher: ObjectWatcher,
    configProvider: () -> AppWatcher.Config
  ) {
    //fragmentDestroyWatchers列表,支持不同Fragment实例的检测;
    //这儿的watcher都承继自(Activity)->Unit表明办法类型/函数类型,
    //参数为Activity,回来值为空;因为是办法类型所以需求重写invoke办法
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
    //Android O后构建AndroidOFragmentDestroyWatcher
    if (SDK_INT >= O) {
      fragmentDestroyWatchers.add(
          AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
      )
    }
    //假如Class.for(className)能找到androidx.fragment.app.Fragment和
    //leakcanary.internal.AndroidXFragmentDestroyWatcher则增加AndroidXFragmentDestroyWatcher则增加
    getWatcherIfAvailable(
        ANDROIDX_FRAGMENT_CLASS_NAME,
        ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
        objectWatcher,
        configProvider
    )?.let {
      fragmentDestroyWatchers.add(it)
    }
    //假如Class.for(className)能找到android.support.v4.app.Fragment和
    //leakcanary.internal.AndroidSupportFragmentDestroyWatcher则增加AndroidSupportFragmentDestroyWatcher
    getWatcherIfAvailable(
        ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
        ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
        objectWatcher,
        configProvider
    )?.let {
      fragmentDestroyWatchers.add(it)
    }
    if (fragmentDestroyWatchers.size == 0) {
      return
    }
    //注册Activity生命周期回调,在Activity的onActivityCreated()办法中遍历这些watcher办法类型,实践调用的是对应的invoke办法
    application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityCreated(
        activity: Activity,
        savedInstanceState: Bundle?
      ) {
        for (watcher in fragmentDestroyWatchers) {
          //实践调用的是对应的invoke办法
          watcher(activity)
        }
      }
    })
  }

假如体系是Android O今后版本,运用AndroidOFragmentDestroyWatcher,假如app运用的是androidx中的fragment,则增加对应的AndroidXFragmentDestroyWatcher,假如运用support库中的fragment,则增加AndroidSupportFragmentDestroyWatcher。终究在invoke办法中运用对应的fragmentManager注册Fragment的生命周期回调,在onFragmentViewDestroyed()和onFragmentDestroyed()办法中运用ObjectWatcher来检测fragment。下面以AndroidXFragmentDestroyWatcher为例:

先看一下AndroidXFragmentDestroyWatcher的invoke办法完结:

override fun invoke(activity: Activity) {
    if (activity is FragmentActivity) {
      //获得对应的FragmentManager,注册生命周期回调
      val supportFragmentManager = activity.supportFragmentManager
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
      //增加了ViewModelStoreOwner为Activity的ViewModelClearedWatcher监测
      ViewModelClearedWatcher.install(activity, objectWatcher, configProvider)
    }
  }

LeakCanary在onFragmentDestroyed回调里边来处理查看Fragment是否正常被收回的检测逻辑。

override fun onFragmentDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      if (configProvider().watchFragments) {
        objectWatcher.watch(
            fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
        )
      }
    }

Fragment的View 收回监听

LeakCanary在onFragmentViewDestroyed回调里边来处理查看Fragment的View是否正常被收回的检测逻辑。

override fun onFragmentViewDestroyed(
      fm: FragmentManager,
      fragment: Fragment
    ) {
      val view = fragment.view
      if (view != null && configProvider().watchFragmentViews) {
        objectWatcher.watch(
            view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
            "(references to its views should be cleared to prevent leaks)"
        )
      }
    }

RootView 监控

因为 Android Framework 未供给设置大局监听 RootView 从 WindowManager 中移除的办法,所以 LeakCanary 是经过 Hook 的办法完结的,这一块是经过 squareup 另一个开源库curtains完结的。 RootView 监控这部分源码也比较复杂了,需求经过 2 步 Hook 来完结:

  • 1、Hook WMS 服务内部的WindowManagerGlobal.mViewsRootView 列表,获取 RootView 新增和移除的机遇;
  • 2、查看 View 对应的 Window 类型,假如是 Dialog 或 DreamService 等类型,则在注册View#addOnAttachStateChangeListener()监听,在其间的 onViewDetachedFromWindow() 回调中将 View 方针交给 ObjectWatcher 监控。

LeakCanary 源码摘要如下:

RootViewWatcher.kt

override fun install() {
    // 1. 注册 RootView 监听
    Curtains.onRootViewsChangedListeners += listener
}
private val listener = OnRootViewAddedListener { rootView ->
    val trackDetached = when(rootView.windowType) {
    PHONE_WINDOW -> {
        when (rootView.phoneWindow?.callback?.wrappedCallback) {
            // Activity 类型现已在 ActivityWatcher 中监控了,不需求重复监控
            is Activity -> false
            is Dialog -> {
                // leak_canary_watcher_watch_dismissed_dialogs:Dialog 监控开关
                val resources = rootView.context.applicationContext.resources
                resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
            }
            // DreamService 屏保等
            else -> true
        }
    }
    POPUP_WINDOW -> false
    TOOLTIP, TOAST, UNKNOWN -> true
    }
    if (trackDetached) {
        // 2. 注册 View#addOnAttachStateChangeListener 监听
        rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
            val watchDetachedView = Runnable {
                // 3. 交给 ObjectWatcher 监控
                reachabilityWatcher.expectWeaklyReachable(rootView /*被监控方针*/ , "${rootView::class.java.name} received View#onDetachedFromWindow() callback")
            }
            override fun onViewAttachedToWindow(v: View) {
                mainHandler.removeCallbacks(watchDetachedView)
            }
            override fun onViewDetachedFromWindow(v: View) {
                mainHandler.post(watchDetachedView)
            }
        })
    }
}

curtains 源码摘要如下:

RootViewsSpy.kt

private val delegatingViewList = object : ArrayList<View>() {
    // 重写 ArrayList#add 办法
    override fun add(element: View): Boolean {
        // 回调
        listeners.forEach { it.onRootViewsChanged(element, true) }
        return super.add(element)
    }
    // 重写 ArrayList#removeAt 办法
    override fun removeAt(index: Int): View {
        // 回调
        val removedView = super.removeAt(index)
        listeners.forEach { it.onRootViewsChanged(removedView, false) }
        return removedView
    }
}
companion object {
    fun install(): RootViewsSpy {
        return RootViewsSpy().apply {
            WindowManagerSpy.swapWindowManagerGlobalMViews { mViews /*原方针*/ ->
                // 新方针(lambda 表达式的末行便是回来值)
                delegatingViewList.apply { addAll(mViews) }
            }
        }
    }
}

WindowManageSpy.kt

// Hook WMS 服务内部的 WindowManagerGlobal.mViews RootView 列表
// swap 是一个 lambda 表达式,参数为原方针,回来值为注入的新方针
fun swapWindowManagerGlobalMViews(swap: (ArrayList<View>) -> ArrayList<View>) {
    windowManagerInstance?.let { windowManagerInstance ->
        mViewsField?.let { mViewsField ->
            val mViews = mViewsField[windowManagerInstance] as ArrayList<View>
            mViewsField[windowManagerInstance] = swap(mViews)
        }
    }
}

Service收回监听

因为 Android Framework 未供给设置 Service#onDestroy() 大局监听的办法,所以 LeakCanary 是经过 Hook 的办法完结的。

Service 监控这部分源码比较复杂了,需求经过 2 步 Hook 来完结:

  • 1、Hook 主线程音讯循环的mH.mCallback回调,监听其间的 STOP_SERVICE 音讯,将行将 Destroy 的 Service 方针暂存起来(因为 ActivityThread.H 中没有 DESTROY_SERVICE 音讯,所以不能直接监听到 onDestroy() 事情,需求第 2 步);
  • 2、运用动态署理 Hook AMS 与 App 通讯的的IActivityManagerBinder 方针,署理其间的serviceDoneExecuting()办法,视为 Service#onDestroy() 的履行机遇,拿到暂存的 Service 方针交给 ObjectWatcher 监控。

ServiceWatcher.kt源码摘要如下:

private var uninstallActivityThreadHandlerCallback: (() -> Unit)? = null
// 暂存行将 Destroy 的 Service
private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
override fun install() {
    // 1. Hook mH.mCallback
    swapActivityThreadHandlerCallback { mCallback /*原方针*/ ->
        // uninstallActivityThreadHandlerCallback:用于撤销 Hook
        uninstallActivityThreadHandlerCallback = {
            swapActivityThreadHandlerCallback {
                mCallback
            }
        }
        // 新方针(lambda 表达式的末行便是回来值)
        Handler.Callback { msg ->
            // 1.1 Service#onStop() 事情
            if (msg.what == STOP_SERVICE) {
                val key = msg.obj as IBinder
                // 1.2 activityThreadServices:反射获取 ActivityThread mServices 映射表 <IBinder, CreateServiceData>
                activityThreadServices[key]?.let {
                    // 1.3 暂存行将 Destroy 的 Service
                    servicesToBeDestroyed[token] = WeakReference(service)
                }
            }
            // 1.4 持续履行 Framework 原有逻辑
            mCallback?.handleMessage(msg) ?: false
        }
    }
    // 2. Hook AMS IActivityManager
    swapActivityManager { activityManagerInterface, activityManagerInstance /*原方针*/ ->
        // uninstallActivityManager:用于撤销 Hook
        uninstallActivityManager = {
            swapActivityManager { _, _ ->
                activityManagerInstance
            }
        }
        // 新方针(lambda 表达式的末行便是回来值)
        Proxy.newProxyInstance(activityManagerInterface.classLoader, arrayOf(activityManagerInterface)) { _, method, args ->
            // 2.1 署理 serviceDoneExecuting() 办法
            if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
                // 2.2 取出暂存的行将 Destroy 的 Service
                val token = args!![0] as IBinder
                if (servicesToBeDestroyed.containsKey(token)) {
                    servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
                        // 2.3 交给 ObjectWatcher 监控
                        serviceWeakReference.get()?.let { service ->
                            reachabilityWatcher.expectWeaklyReachable(service /*被监控方针*/, "${service::class.java.name} received Service#onDestroy() callback")
                        }
                    }
                }
            }
            // 2.4 持续履行 Framework 原有逻辑
            method.invoke(activityManagerInstance, *args)
        }
    }
}
override fun uninstall() {
    // 关闭 mH.mCallback 的 Hook
    uninstallActivityManager?.invoke()
    uninstallActivityThreadHandlerCallback?.invoke()
    uninstallActivityManager = null
    uninstallActivityThreadHandlerCallback = null
}
// 运用反射修正 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?
    // 将 swap 的回来值作为新方针,完结 Hook
    mCallbackField[mH] = swap(mCallback)
}
// 运用反射修正 AMS 与 App 通讯的 IActivityManager Binder 方针
// swap 是一个 lambda 表达式,参数为 IActivityManager 的 Class 方针和接口原完结方针,回来值为注入的新方针
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]
    // Calling get() instead of reading from the field directly to ensure the singleton is
    // created.
    val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)
    val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
    // 将 swap 的回来值作为新方针,完结 Hook
    mInstanceField[activityManagerSingletonInstance] = swap(iActivityManagerInterface, activityManagerInstance!!)
}

至此,LeakCanary 初始化完结,并且成功在 Android Framework 的各个方位安插监控,完结对 Activity 和 Service 等方针进入无用状况的监听。咱们能够用一张示意图描述 LeakCanary 的部分结构:

LeakCanary源码分析(2.10最新版)

4、LeakCanary 怎样断定方针走漏

在以上进程中,当方针的运用生命周期结束后,会交给ObjectWatcher监控,现在咱们来具体看下它是怎样判别方针产生走漏的。首要逻辑概括为 3 步:

  • 第 1 步: 为被监控方针watchedObject创立一个KeyedWeakReference弱引证,并存储到 <UUID, KeyedWeakReference> 的映射表中;
  • 第 2 步: postDelay 五秒后查看引证方针是否出现在引证行列中,出现在行列则说明被监控方针未产生走漏。随后,移除映射表中未走漏的记载,更新走漏的引证方针的retainedUptimeMillis字段以符号为走漏;
  • 第 3 步: 经过回调onObjectRetained奉告 LeakCanary 内部产生新的内存走漏。

源码摘要如下:

AppWatcher.kt

val objectWatcher = ObjectWatcher(
    // lambda 表达式获取当时体系时刻
    clock = { SystemClock.uptimeMillis() },
    // lambda 表达式完结 Executor SAM 接口
    checkRetainedExecutor = {
        mainHandler.postDelayed(it, retainedDelayMillis)
    },
    // lambda 表达式获取监控开关
    isEnabled = { true }
)

ObjectWatcher.kt

class ObjectWatcher constructor(
    private val clock: Clock,
    private val checkRetainedExecutor: Executor,
    private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {
    if (!isEnabled()) {
        // 监控开关
        return
    }
    // 被监控的方针映射表 <UUID,KeyedWeakReference>
    private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
    // KeyedWeakReference 相关的引证行列,用于判别方针是否走漏
    private val queue = ReferenceQueue<Any>()
    // 1. 为 watchedObject 方针增加监控
    @Synchronized 
    override fun expectWeaklyReachable(
        watchedObject: Any,
        description: String
    ) {
        // 1.1 移除 watchedObjects 中未走漏的引证方针
        removeWeaklyReachableObjects()
        // 1.2 新建一个 KeyedWeakReference 引证方针
        val key = UUID.randomUUID().toString()
        val watchUptimeMillis = clock.uptimeMillis()
        watchedObjects[key] = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
        // 2. 五秒后查看引证方针是否出现在引证行列中,不然断定产生走漏
        // checkRetainedExecutor 相当于 postDelay 五秒后履行 moveToRetained() 办法
        checkRetainedExecutor.execute {
            moveToRetained(key)
        }
    }
    // 2. 五秒后查看引证方针是否出现在引证行列中,不然说明产生走漏
    @Synchronized 
    private fun moveToRetained(key: String) {
        // 2.1 移除 watchedObjects 中未走漏的引证方针
        removeWeaklyReachableObjects()
        // 2.2 依然存在的引证方针被断定产生走漏
        val retainedRef = watchedObjects[key]
        if (retainedRef != null) {
            retainedRef.retainedUptimeMillis = clock.uptimeMillis()
            // 3. 回调告诉 LeakCanary 内部处理
            onObjectRetainedListeners.forEach { it.onObjectRetained() }
        }
    }
    // 移除未走漏方针对应的 KeyedWeakReference
    private fun removeWeaklyReachableObjects() {
        var ref: KeyedWeakReference?
        do {
            ref = queue.poll() as KeyedWeakReference?
            if (ref != null) {
                // KeyedWeakReference 出现在引证行列中,说明未产生走漏
                watchedObjects.remove(ref.key)
            }
        } while (ref != null)
    }
    // 4. Heap Dump 后移除一切监控时刻早于 heapDumpUptimeMillis 的引证方针
    @Synchronized 
    fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) {
        val weakRefsToRemove = watchedObjects.filter { it.value.watchUptimeMillis <= heapDumpUptimeMillis }
        weakRefsToRemove.values.forEach { it.clear() }
        watchedObjects.keys.removeAll(weakRefsToRemove.keys)
    }
    // 获取是否有内存走漏方针
    val hasRetainedObjects: Boolean
    @Synchronized get() {
        // 移除 watchedObjects 中未走漏的引证方针
        removeWeaklyReachableObjects()
        return watchedObjects.any { it.value.retainedUptimeMillis != -1L }
    }
    // 获取内存走漏方针计数
    val retainedObjectCount: Int
    @Synchronized get() {
        // 移除 watchedObjects 中未走漏的引证方针
        removeWeaklyReachableObjects()
        return watchedObjects.count { it.value.retainedUptimeMillis != -1L }
    }
}

被监控方针watchedObject相关的弱引证方针:

KeyedWeakReference.kt

class KeyedWeakReference(
    // 被监控方针
    referent: Any,
    // 仅有 Key,依据此字段匹配映射表中的记载
    val key: String,
    // 描述信息
    val description: String,
    // 监控开端时刻,即引证方针创立时刻
    val watchUptimeMillis: Long,
    // 相关的引证行列
    referenceQueue: ReferenceQueue<Any>
) : WeakReference<Any>(referent, referenceQueue) {
    // 记载实践方针 referent 被断定为走漏方针的时刻
    // -1L 表明非走漏方针,或许还未断定完结
    @Volatile
    var retainedUptimeMillis = -1L
    override fun clear() {
        super.clear()
        retainedUptimeMillis = -1L
    }
    companion object {
        // 记载最近一次触发 Heap Dump 的时刻
        @Volatile
        @JvmStatic var heapDumpUptimeMillis = 0L
    }
}

5、LeakCanary 何时进行剖析

ObjectWatcher 断定被监控方针产生走漏后,会经过接口办法OnObjectRetainedListener#onObjectRetained()回调到 LeakCanary 内部的管理器 InternalLeakCanary 处理(在前文 AppWatcher 初始化中提到过)。LeakCanary 不会每次发现内存走漏方针都进行剖析工作,而会进行两个阻拦:

  • 阻拦 1:走漏方针计数未到达阈值,或许进入后台时刻未到达阈值;
  • 阻拦 2:核算距离上一次 HeapDump 未超越 60s。

源码摘要如下:

InternalLeakCanary.kt

// 从 ObjectWatcher 回调过来
override fun onObjectRetained() = scheduleRetainedObjectCheck()
private lateinit var heapDumpTrigger: HeapDumpTrigger
fun scheduleRetainedObjectCheck() {
    if (this::heapDumpTrigger.isInitialized) {
        heapDumpTrigger.scheduleRetainedObjectCheck()
    }
}

HeapDumpTrigger.kt

fun scheduleRetainedObjectCheck(delayMillis: Long = 0L) {
    // 已简化:源码此处运用时刻戳阻拦,防止重复 postDelayed
    backgroundHandler.postDelayed({
        checkRetainedObjects()
    }, delayMillis)
}
private fun checkRetainedObjects() {
    val config = configProvider()
    // 走漏方针计数
    var retainedReferenceCount = objectWatcher.retainedObjectCount
    if (retainedReferenceCount > 0) {
        // 主动触发 GC,并等待 100 ms
        gcTrigger.runGc()
        // 从头获取走漏方针计数
        retainedReferenceCount = objectWatcher.retainedObjectCount
    }
    // 阻拦 1:走漏方针计数未到达阈值,或许进入后台时刻未到达阈值
    if (retainedKeysCount < retainedVisibleThreshold) {
        // App 坐落前台或许刚刚进入后台
        if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
            // 发送告诉提醒
            showRetainedCountNotification("App visible, waiting until %d retained objects")
            // 延迟 2 秒再查看
            scheduleRetainedObjectCheck(WAIT_FOR_OBJECT_THRESHOLD_MILLIS)
            return;
        }
    }
    // 阻拦 2:核算距离上一次 HeapDump 未超越 60s
    val now = SystemClock.uptimeMillis()
    val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
    if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
        // 发送告诉提醒
        showRetainedCountNotification("Last heap dump was less than a minute ago")
        // 延迟 (60 - elapsedSinceLastDumpMillis)s 再查看
        scheduleRetainedObjectCheck(WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis)
        return
    }
    // 移除告诉提醒
    dismissRetainedCountNotification()
    // 触发 HeapDump(此时,使用有或许在后台)
    dumpHeap(...)
}
// 真实开端履行 Heap Dump
private fun dumpHeap(...) {
    // 1. 获取文件存储供给器
    val directoryProvider = InternalLeakCanary.createLeakDirectoryProvider(InternalLeakCanary.application)
    // 2. 创立 .hprof File 文件
    val heapDumpFile = directoryProvider.newHeapDumpFile()
    // 3. 履行 Heap Dump
    // Heap Dump 开端时刻戳
    val heapDumpUptimeMillis = SystemClock.uptimeMillis()
    // heapDumper.dumpHeap:终究调用 Debug.dumpHprofData(heapDumpFile.absolutePath) 
    configProvider().heapDumper.dumpHeap(heapDumpFile)
    // 4. 清除 ObjectWatcher 中过期的监控
    objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
    // 5. 剖析堆快照
    InternalLeakCanary.sendEvent(HeapDump(currentEventUniqueId!!, heapDumpFile, durationMillis, reason))
}

恳求 GC 的源码能够看一眼:

GcTrigger.kt

fun interface GcTrigger {
    fun runGc()
    object Default : GcTrigger {
        override fun runGc() {
            // Runtime.gc() 比较于 System.gc() 更有或许触发 GC
            Runtime.getRuntime().gc()
            // 暂停等待 GC 
            Thread.sleep(100)
            System.runFinalization()
        }
    }
}

在前面的工作中,LeakCanary 现已成功生成.hprof堆快照文件,并且发送了一个 LeakCanary 内部事情HeapDump。那么这个事情在哪里被消费的呢?

一步步跟踪代码能够看到 LeakCanary 的装备项中设置了多个事情顾客 EventListener,其间与 HeapDump 事情有关的是when{}代码块中三个顾客。不过,这三个顾客并不是并存的,而是会依据 App 当时的依靠项而选择最优的履行战略:

  • 战略 1 – WorkerManager 多进程剖析
  • 战略 2 – WorkManager 异步剖析
  • 战略 3 – 异步线程剖析(兜底战略)

LeakCanary 装备项中的事情顾客:

LeakCanary.kt

data class Config(
    val eventListeners: List<EventListener> = listOf(
        LogcatEventListener,
        ToastEventListener,
        LazyForwardingEventListener {
            if (InternalLeakCanary.formFactor == TV) TvEventListener else NotificationEventListener
        },
        when {
            // 战略 1 - WorkerManager 多进程剖析
            RemoteWorkManagerHeapAnalyzer.remoteLeakCanaryServiceInClasspath ->RemoteWorkManagerHeapAnalyzer
            // 战略 2 - WorkManager 异步剖析
            WorkManagerHeapAnalyzer.validWorkManagerInClasspath -> WorkManagerHeapAnalyzer
            // 战略 3 - 异步线程剖析(兜底战略)
            else -> BackgroundThreadHeapAnalyzer
        }
    ),
    ...
)

战略 1 – WorkerManager 多进程剖析

判别是否能够类加载RemoteLeakCanaryWorkerService,这个类坐落前文提到的com.squareup.leakcanary:leakcanary-android-process:2.9.1依靠中。假如能够类加载成功则视为有依靠,运用 WorkerManager 多进程剖析;

RemoteWorkManagerHeapAnalyzer.kt

object RemoteWorkManagerHeapAnalyzer : EventListener {
    // 经过类加载是否成功,判别是否存在依靠
    internal val remoteLeakCanaryServiceInClasspath by lazy {
        try {
            Class.forName("leakcanary.internal.RemoteLeakCanaryWorkerService")
            true
        } catch (ignored: Throwable) {
            false
        }
    }
    override fun onEvent(event: Event) {
        if (event is HeapDump) {
            // 创立并分发 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()
            WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
        }
    }
}

RemoteHeapAnalyzerWorker.kt

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 {
            // 1.1 剖析堆快照
            val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(heapDump, isCanceled = {
                result.isCancelled
            }) { progressEvent ->
                // 1.2 发送剖析进展事情
                if (!result.isCancelled) {
                    InternalLeakCanary.sendEvent(progressEvent)
                }
            }
            // 1.3 发送剖析完结事情
            InternalLeakCanary.sendEvent(doneEvent)
            result.set(Result.success())
        }
        return result
    }
}

战略 2 – WorkManager 异步剖析:

判别是否能够类加载androidx.work.WorkManager,假如能够,则运用 WorkManager 异步剖析;

WorkManagerHeapAnalyzer.kt

internal val validWorkManagerInClasspath by lazy {
    // 判别 WorkManager 依靠,代码略
}
override fun onEvent(event: Event) {
    if (event is HeapDump) {
        // 创立并分发 WorkManager 恳求
        val heapAnalysisRequest = OneTimeWorkRequest.Builder(HeapAnalyzerWorker::class.java).apply {
            setInputData(event.asWorkerInputData())
            addExpeditedFlag()
        }.build()
        val application = InternalLeakCanary.application
        WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
    }
}

HeapAnalyzerWorker.kt

internal class HeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
    override fun doWork(): Result {
        // 2.1 剖析堆快照
        val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(inputData.asEvent()) { event ->
            // 2.2 发送剖析进展事情
            InternalLeakCanary.sendEvent(event)
        }
        // 2.3 发送剖析完结事情
        InternalLeakCanary.sendEvent(doneEvent)
        return Result.success()
    }
}

战略 3 – 异步线程剖析(兜底战略):

假如以上战略未射中,则直接运用子线程兜底履行。

BackgroundThreadHeapAnalyzer.kt

object BackgroundThreadHeapAnalyzer : EventListener {
    // HandlerThread
    internal val heapAnalyzerThreadHandler by lazy {
        val handlerThread = HandlerThread("HeapAnalyzer")
        handlerThread.start()
        Handler(handlerThread.looper)
    }
    override fun onEvent(event: Event) {
        if (event is HeapDump) {
            // HandlerThread 恳求
            heapAnalyzerThreadHandler.post {
                // 3.1 剖析堆快照
                val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(event) { event ->
                    // 3.2 发送剖析进展事情
                    InternalLeakCanary.sendEvent(event)
                }
                // 3.3 发送剖析完结事情
                InternalLeakCanary.sendEvent(doneEvent)
            }
        }
    }
}

能够看到,不管采用那种履行战略,终究履行的逻辑都是相同的:

  • 1、剖析堆快照;
  • 2、发送剖析进展事情;
  • 3、发送剖析完结事情。

6、LeakCanary 怎样剖析堆快照

在前面的剖析中,咱们现已知道 LeakCanary 是经过子线程或许子进程履行AndroidDebugHeapAnalyzer.runAnalysisBlocking办法来剖析堆快照的,并在剖析进程中和剖析完结后发送回调事情。现在咱们来阅读 LeakCanary 的堆快照剖析进程:

AndroidDebugHeapAnalyzer.kt

fun runAnalysisBlocking(
    heapDumped: HeapDump,
    isCanceled: () -> Boolean = { false },
    progressEventListener: (HeapAnalysisProgress) -> Unit
): HeapAnalysisDone<*> {
    ...
    // 1. .hprof 文件
    val heapDumpFile = heapDumped.file
    // 2. 剖析堆快照
    val heapAnalysis = analyzeHeap(heapDumpFile, progressListener, isCanceled)
    val analysisDoneEvent = ScopedLeaksDb.writableDatabase(application) { db ->
    // 3. 将剖析陈述持久化到 DB
    val id = HeapAnalysisTable.insert(db, heapAnalysis)
    // 4. 发送剖析完结事情(回来到上一级进行发送:InternalLeakCanary.sendEvent(doneEvent))
    val showIntent = LeakActivity.createSuccessIntent(application, id)
    val leakSignatures = fullHeapAnalysis.allLeaks.map { it.signature }.toSet()
    val leakSignatureStatuses = LeakTable.retrieveLeakReadStatuses(db, leakSignatures)
    val unreadLeakSignatures = leakSignatureStatuses.filter { (_, read) -> !read}.keys.toSet()
        HeapAnalysisSucceeded(heapDumped.uniqueId, fullHeapAnalysis, unreadLeakSignatures ,showIntent)
    }
    return analysisDoneEvent
}

中心剖析办法是analyzeHeap(…),持续往下走:

AndroidDebugHeapAnalyzer.kt

private fun analyzeHeap(
    heapDumpFile: File,
    progressListener: OnAnalysisProgressListener,
    isCanceled: () -> Boolean
): HeapAnalysis {
    ...
    // Shark 堆快照剖析器
    val heapAnalyzer = HeapAnalyzer(progressListener)
    ...
    // 构建方针图信息
    val sourceProvider = ConstantMemoryMetricsDualSourceProvider(ThrowingCancelableFileSourceProvider(heapDumpFile)
    val graph = sourceProvider.openHeapGraph(proguardMapping = proguardMappingReader?.readProguardMapping())
    ...
    // 开端剖析
    heapAnalyzer.analyze(
    heapDumpFile = heapDumpFile,
    graph = graph,
    leakingObjectFinder = config.leakingObjectFinder, // 默许是 KeyedWeakReferenceFinder
    referenceMatchers = config.referenceMatchers, // 默许是 AndroidReferenceMatchers
    computeRetainedHeapSize = config.computeRetainedHeapSize, // 默许是 true
    objectInspectors = config.objectInspectors, // 默许是 AndroidObjectInspectors
    metadataExtractor = config.metadataExtractor // 默许是 AndroidMetadataExtractor
    )
}

开端进入 Shark 组件:

shark.HeapAnalyzer.kt

// analyze -> analyze -> FindLeakInput.analyzeGraph
private fun FindLeakInput.analyzeGraph(
    metadataExtractor: MetadataExtractor,
    leakingObjectFinder: LeakingObjectFinder,
    heapDumpFile: File,
    analysisStartNanoTime: Long
): HeapAnalysisSuccess {
    ...
    // 1. 在堆快照中寻觅走漏方针,默许是寻觅 KeyedWeakReference 类型方针
    // leakingObjectFinder 默许是 KeyedWeakReferenceFinder
    val leakingObjectIds = leakingObjectFinder.findLeakingObjectIds(graph)
    // 2. 剖析走漏方针的最短引证链,并依照使用链签名分类
    // applicationLeaks: Application Leaks
    // librbuildLeakTracesaryLeaks:Library Leaks
    // unreachableObjects:LeakCanary 无法剖分出强引证链,能够提 Stack Overflow
    val (applicationLeaks, libraryLeaks, unreachableObjects) = findLeaks(leakingObjectIds)
    // 3. 回来剖析完结事情
    return HeapAnalysisSuccess(...)
}
private fun FindLeakInput.findLeaks(leakingObjectIds: Set<Long>): LeaksAndUnreachableObjects {
    // PathFinder:引证链剖析器
    val pathFinder = PathFinder(graph, listener, referenceReader, referenceMatchers)
    // pathFindingResults:完好引证链
    val pathFindingResults = pathFinder.findPathsFromGcRoots(leakingObjectIds, computeRetainedHeapSize)
    // unreachableObjects:LeakCanary 无法剖分出强引证链(相当于 LeakCanary 的 Bug)
    val unreachableObjects = findUnreachableObjects(pathFindingResults, leakingObjectIds)
    // shortestPaths:最短引证链
    val shortestPaths = deduplicateShortestPaths(pathFindingResults.pathsToLeakingObjects)
    // inspectedObjectsByPath:符号信息
    val inspectedObjectsByPath = inspectObjects(shortestPaths)
    // retainedSizes:走漏内存大小
    val retainedSizes = computeRetainedSizes(inspectedObjectsByPath, pathFindingResults.dominatorTree)
    // 生成单个走漏问题的剖析陈述,并依照使用链签名分组,依照 Application Leaks 和 Library Leaks 分类,依照 Application Leaks 和 Library Leaks 分类
    // applicationLeaks: Application Leaks
    // librbuildLeakTracesaryLeaks:Library Leaks
    val (applicationLeaks, librbuildLeakTracesaryLeaks) = buildLeakTraces(shortestPaths, inspectedObjectsByPath, retainedSizes)
    return LeaksAndUnreachableObjects(applicationLeaks, libraryLeaks, unreachableObjects)
}

能够看到,堆快照剖析终究是交给 Shark 中的 HeapAnalizer 完结的,中心流程是:

  • 1、在堆快照中寻觅走漏方针,默许是寻觅 KeyedWeakReference 类型方针;
  • 2、剖析 KeyedWeakReference 方针的最短引证链,并依照引证链签名分组,依照 Application Leaks 和 Library Leaks 分类;
  • 3、回来剖析完结事情。

第 1 步和第 3 步不用说了,持续剖析最复杂的第 2 步:

shark.HeapAnalyzer.kt

// 生成单个走漏问题的剖析陈述,并依照使用链签名分组,依照 Application Leaks 和 Library Leaks 分类,依照 Application Leaks 和 Library Leaks 分类
private fun FindLeakInput.buildLeakTraces(
    shortestPaths: List<ShortestPath> /*最短引证链*/ ,
    inspectedObjectsByPath: List<List<InspectedObject>> /*符号信息*/ ,
    retainedSizes: Map<Long, Pair<Int, Int>>? /*走漏内存大小*/
): Pair<List<ApplicationLeak>, List<LibraryLeak>> {
    // Application Leaks
    val applicationLeaksMap = mutableMapOf<String, MutableList<LeakTrace>>()
    // Library Leaks
    val libraryLeaksMap = mutableMapOf<String, Pair<LibraryLeakReferenceMatcher, MutableList<LeakTrace>>>()
    shortestPaths.forEachIndexed { pathIndex, shortestPath ->
        // 符号信息
        val inspectedObjects = inspectedObjectsByPath[pathIndex]
        // 实例化引证链上的每个方针快照(非置疑方针的 leakingStatus 为 NOT_LEAKING)
        val leakTraceObjects = buildLeakTraceObjects(inspectedObjects, retainedSizes)
        val referencePath = buildReferencePath(shortestPath, leakTraceObjects)
        // 剖析陈述
        val leakTrace = LeakTrace(
            gcRootType = GcRootType.fromGcRoot(shortestPath.root.gcRoot),
            referencePath = referencePath,
            leakingObject = leakTraceObjects.last()
        )
        val firstLibraryLeakMatcher = shortestPath.firstLibraryLeakMatcher()
        if (firstLibraryLeakMatcher != null) {
            // Library Leaks
            val signature: String = firstLibraryLeakMatcher.pattern.toString().createSHA1Hash()
            libraryLeaksMap.getOrPut(signature) { firstLibraryLeakMatcher to mutableListOf() }.second += leakTrace
        } else {
            // Application Leaks
            applicationLeaksMap.getOrPut(leakTrace.signature) { mutableListOf() } += leakTrace
        }
    }
    val applicationLeaks = applicationLeaksMap.map { (_, leakTraces) ->
        // 实例化为 ApplicationLeak 类型
        ApplicationLeak(leakTraces)
    }
    val libraryLeaks = libraryLeaksMap.map { (_, pair) ->
        // 实例化为 LibraryLeak 类型
        val (matcher, leakTraces) = pair
        LibraryLeak(leakTraces, matcher.pattern, matcher.description)
    }
    return applicationLeaks to libraryLeaks
}

LeakCanary怎样挑选置疑方针

LeakCanary 会运用 ObjectInspector 方针检索器在引证链上的节点中符号必要的信息和状况,符号信息会显现在剖析陈述中,并且会影响陈述中的提示。而引证链LEAKING节点今后到第一个NOT_LEAKING节点中心的节点,才会用~~~下划线符号为置疑方针。

LeakCanary 经过leakingObjectFinder符号引证信息,leakingObjectFinder 默许是AndroidObjectInspectors.appDefaults,也能够在装备项中自定义。

// inspectedObjectsByPath:挑选出非置疑方针(剖析陈述中 ~~~ 符号的是置疑方针)
val inspectedObjectsByPath = inspectObjects(shortestPaths)

看一下可视化陈述中相关源码:

DisplayLeakAdapter.kt

...
val reachabilityString = when (leakingStatus) {
    UNKNOWN -> extra("UNKNOWN")
    NOT_LEAKING -> "NO" + extra(" (${leakingStatusReason})")
    LEAKING -> "YES" + extra(" (${leakingStatusReason})")
}
...

LeakTrace.kt

// 是否为置疑方针
fun referencePathElementIsSuspect(index: Int): Boolean {
    return  when (referencePath[index].originObject.leakingStatus) {
        UNKNOWN -> true
        NOT_LEAKING -> index == referencePath.lastIndex || referencePath[index + 1].originObject.leakingStatus != NOT_LEAKING
        else -> false
    }
}

LeakCanary 剖析完结后的处理

有两个方位处理了HeapAnalysisSucceeded事情:

  • Logcat:打印剖析陈述日志;
  • Notification: 发送剖析成功体系告诉音讯。

LogcatEventListener.kt

object LogcatEventListener : EventListener {
    ...
    SharkLog.d { "\u200B\n${LeakTraceWrapper.wrap(event.heapAnalysis.toString(), 120)}" }
    ...
}

NotificationEventListener.kt

object NotificationEventListener : EventListener {
    ...
    val flags = if (Build.VERSION.SDK_INT >= 23) {
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    } else {
        PendingIntent.FLAG_UPDATE_CURRENT
    }
    // 点击告诉音讯打开可视化剖析陈述
    val pendingIntent = PendingIntent.getActivity(appContext, 1,  event.showIntent, flags)
    showHeapAnalysisResultNotification(contentTitle,pendingIntent)
    ...
}

至此,LeakCanary 原理剖析结束。

7、 总结

最后来总结下LeakCanary内存走漏剖析进程吧(Activity):

(1)注册监听Activity生命周期onDestroy事情

(2)在Activity onDestroy事情回调中创立KeyedWeakReference方针,并相关ReferenceQueue

(3)延时5秒查看方针方针是否收回

(4)未收回则敞开服务,dump heap获取内存快照hprof文件

(5)解析hprof文件依据KeyedWeakReference类型过滤找到内存走漏方针

(6)核算方针到GC roots的最短途径,并兼并一切最短途径为一棵树

(7)输出剖析成果,并依据剖析成果展现到可视化页面

LeakCanary源码分析(2.10最新版)