首页 Android三方开源库之LeakCanary2.4源码分析
文章
取消

Android三方开源库之LeakCanary2.4源码分析

LeanCanary内部主要使用了Reference以及ReferenceQueue配合来实现对象被回收时的监听,这是它的核心逻辑,因此我们先了解下这部分内容:

Reference

Reference是一个泛型抽象类,其中软引用、弱引用、虚引用都继承自它,它主要有几个成员变量:

  • 泛型引用对象 T:被回收时被赋值为null
  • 引用队列 ReferenceQueue:一个单链表实现的队列,保存即将被回收的引用对象
  • queueNext:指向下一个待处理的Reference引用对象
  • pendingNext:指向下一个待入列的Reference引用对象

ReferenceQueue

Reference配合ReferenceQueue就可以实现对象回收监听,示例代码如下:

1
2
3
4
5
6
7
8
//创建一个引用队列
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());

被回收后的对象如果在引用队列中找到该引用,则说明对象被回收了,否则就表明该引用有内存泄漏的风险,这也就是LeakCanary的基本原理。

源码分析

LeakCanary在2.0之前版本,通过install方法来完成初始化,但是2.0之后,内部继承ContentProvider完成初始化工作,我们知道App启动后调用一系列声明周期方法:Application->attachBaseContext =====>ContentProvider->onCreate =====>Application->onCreate =====>Activity->onCreate,可见这里面会调用到ContentProvider的onCreate方法,我们的初始化工作在这里面完成就可以了:

找到leakcanary-object-watcher-android项目的manifest文件,可以看到:

1
2
3
4
5
6
7
<application>
    <provider
         android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
         android:authorities="${applicationId}.leakcanary-installer"
         android:enabled="@bool/leak_canary_watcher_auto_install"
         android:exported="false" />
</application>

AppWatcherInstaller该类继承自ContentProvider,App启动后调用它的onCreate方法:

1
2
3
4
5
6
7
8
9
// AppWatcherInstaller.kt
internal sealed class AppWatcherInstaller : ContentProvider() {
  override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    // 开始初始化工作
    AppWatcher.manualInstall(application)
    return true
  }
}

紧接着看下AppWatcher的manualInstall方法:

1
2
3
4
5
// AppWatcher.kt

fun manualInstall(application: Application) {
    InternalAppWatcher.install(application)
}

内部又调用了InternalAppWatcher的install方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// InternalAppWatcher.kt

fun install(application: Application) {
    checkMainThread()
    if (this::application.isInitialized) {
        return
    }
    // 日志初始化
    SharkLog.logger = DefaultCanaryLog()
    InternalAppWatcher.application = application

    val configProvider = { AppWatcher.config }
    // Activity内存泄漏监听
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
    // Fragment内存泄漏监听
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
    // 注册内存泄漏事件回调
    onAppWatcherInstalled(application)
}

这个方法中首先检查是否在主线程中,然后判断Application是否完成初始化,经过一系列配置,进入到Activity和Fragment的内存泄漏监听,最后注册回调事件。

Activity内存泄漏监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ActivityDestroyWatcher.kt
internal class ActivityDestroyWatcher private constructor(
  private val objectWatcher: ObjectWatcher,
  private val configProvider: () -> Config
) {

  private val lifecycleCallbacks =
    object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
      override fun onActivityDestroyed(activity: Activity) {
        if (configProvider().watchActivities) {
          objectWatcher.watch(
              activity, "${activity::class.java.name} received Activity#onDestroy() callback"
          )
        }
      }
    }

  companion object {
    fun install(
      application: Application,
      objectWatcher: ObjectWatcher,
      configProvider: () -> Config
    ) {
      val activityDestroyWatcher =
        ActivityDestroyWatcher(objectWatcher, configProvider)
  application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
    }
  }
}

上面代码中,通过调用Application的registerActivityLifecycleCallbacks方法,注册lifecycleCallback,进而可以通过回调获取app的的每一个Activity生命周期变化,这里只重写了onActivityDestroyed方法,原因是在于by noOpDelegate(),通过类委托机制将其他回调实现都交给noOpDelegate,而noOpDelegate是一个空实现的动态代理。在遇到只需要实现接口的部分方法时,就可以这么做,其他方法实现都委托给空实现代理类就好了。

Fragment内存泄漏监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// FragmentDestroyWatcher.kt

fun install(
    application: Application,
    objectWatcher: ObjectWatcher,
    configProvider: () -> AppWatcher.Config
) {
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()

    // Android O后构建AndroidOFragmentDestroyWatcher
    if (SDK_INT >= O) {
        fragmentDestroyWatchers.add(
            AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
        )
    }
	// androidx.fragment.app.Fragment
    getWatcherIfAvailable(
        ANDROIDX_FRAGMENT_CLASS_NAME,
        ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
        objectWatcher,
        configProvider
    )?.let {
        fragmentDestroyWatchers.add(it)
    }
	// android.support.v4.app.Fragment
    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) {
                watcher(activity)
            }
        }
    })
}

Fragment要分情况了:

  • 如果系统是Android O以后版本,使用AndroidOFragmentDestroyWatcher
  • 如果App中使用了androidx中的fragment,则添加对应的AndroidXFragmentDestroyWatcher
  • 如果App中使用了support库中的fragment,则添加AndroidSupportFragmentDestroyWatcher

但是最终都会在invoke方法中使用对应的fragmentManager注册Fragment的生命周期回调,在onFragmentViewDestroyed()和onFragmentDestroyed()方法中使用ObjectWatcher来检测fragment。源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// AndroidOFragmentDestroyWatcher.kt
internal class AndroidOFragmentDestroyWatcher(
  private val objectWatcher: ObjectWatcher,
  private val configProvider: () -> Config
) : (Activity) -> Unit {
  private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {

    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)"
        )
      }
    }

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

  override fun invoke(activity: Activity) {
    val fragmentManager = activity.fragmentManager
    fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
  }
}

这里拿AndroidOFragmentDestroyWatcher源码来看,其他两个类似,从上面可以看出在onFragmentDestroyed回调里面来处理检查Fragment是否正常被回收的检测逻辑,在onFragmentViewDestroyed回调里面来处理检查Fragment的View是否正常被回收的检测逻辑。另外在AndroidXFragmentDestroyWatcher中,LeakCanary增加了对ViewModel的检测,ViewModelClearedWatcher继承自ViewModel,里面使用viewModelMap来存储ViewModelStoreOwner中的ViewModel,并使用伴生对象来初始化自己,关联到ViewModelStoreOwner;在onCleared()方法中使用ObjectWatcher来监测。源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// ViewModelClearedWatcher.kt
internal class ViewModelClearedWatcher(
  storeOwner: ViewModelStoreOwner,
  private val objectWatcher: ObjectWatcher,
  private val configProvider: () -> Config
) : ViewModel() {

  private val viewModelMap: Map<String, ViewModel>?

  init {
    // We could call ViewModelStore#keys with a package spy in androidx.lifecycle instead,
    // however that was added in 2.1.0 and we support AndroidX first stable release. viewmodel-2.0.0
    // does not have ViewModelStore#keys. All versions currently have the mMap field.
    viewModelMap = try {
      val mMapField = ViewModelStore::class.java.getDeclaredField("mMap")
      mMapField.isAccessible = true
      @Suppress("UNCHECKED_CAST")
      mMapField[storeOwner.viewModelStore] as Map<String, ViewModel>
    } catch (ignored: Exception) {
      null
    }
  }

  override fun onCleared() {
    if (viewModelMap != null && configProvider().watchViewModels) {
      viewModelMap.values.forEach { viewModel ->
        objectWatcher.watch(
            viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback"
        )
      }
    }
  }

  companion object {
    fun install(
      storeOwner: ViewModelStoreOwner,
      objectWatcher: ObjectWatcher,
      configProvider: () -> Config
    ) {
      val provider = ViewModelProvider(storeOwner, object : Factory {
        @Suppress("UNCHECKED_CAST")
        override fun <T : ViewModel?> create(modelClass: Class<T>): T =
          ViewModelClearedWatcher(storeOwner, objectWatcher, configProvider) as T
      })
      provider.get(ViewModelClearedWatcher::class.java)
    }
  }
}

综上,我们发现最后都是交给ObjectWatcher来检测Activity、Fragment、Fragment中的View和ViewModel的,这里以Activity为例来看下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// ObjectWatcher.kt
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()

private val queue = ReferenceQueue<Any>()
@Synchronized fun watch(
    watchedObject: Any,
    description: String
) {
    if (!isEnabled()) {
        return
    }
    removeWeaklyReachableObjects()
    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
    checkRetainedExecutor.execute {
        moveToRetained(key)
    }
}

private fun removeWeaklyReachableObjects() {
    // WeakReferences are enqueued as soon as the object to which they point to becomes weakly
    // reachable. This is before finalization or garbage collection has actually happened.
    var ref: KeyedWeakReference?
    do {
        ref = queue.poll() as KeyedWeakReference?
        if (ref != null) {
            watchedObjects.remove(ref.key)
        }
    } while (ref != null)
}

上面首先从watchedObjects集合Map中移除之前回收的引用,这里面的key为UUID,value为包装过的引用对象KeyedWeakReference,它继承自WeakReference:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// KeyedWeakReference.kt
class KeyedWeakReference(
  referent: Any,
  val key: String,
  val description: String,
  val watchUptimeMillis: Long,
  referenceQueue: ReferenceQueue<Any>
) : WeakReference<Any>(
    referent, referenceQueue
) {
  /**
   * Time at which the associated object ([referent]) was considered retained, or -1 if it hasn't
   * been yet.
   */
  @Volatile
  var retainedUptimeMillis = -1L

  companion object {
    @Volatile
    @JvmStatic var heapDumpUptimeMillis = 0L
  }

}

最终会执行一个后台线程来检查在5秒后,引用对象是否回收。

1
2
3
4
5
6
7
// InternalAppWatcher.kt
private val checkRetainedExecutor = Executor {
    mainHandler.postDelayed(it, AppWatcher.config.watchDurationMillis)
}

// AppWatcher.kt
val watchDurationMillis: Long = TimeUnit.SECONDS.toMillis(5)

上面被执行的任务就是moveToRetained方法:

1
2
3
4
5
6
7
8
9
// ObjectWatcher.kt
@Synchronized private fun moveToRetained(key: String) {
    removeWeaklyReachableObjects()
    val retainedRef = watchedObjects[key]
    if (retainedRef != null) {
        retainedRef.retainedUptimeMillis = clock.uptimeMillis()
        onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
}

可以看到这里面会再次执行removeWeaklyReachableObjects方法,将引用队列中的引用对象从监听列表watchedObjects中移除,对于没有被移除的对象,则说明该引用对象未被添加到引用队列,即该引用对象可能存在内存泄漏的风险。最后如果有有内存泄露的引用对象,则遍历执行回调OnObjectRetainedListener的onObjectRetained方法。而onObjectRetained方法在哪实现的呢 ?

我们回到前面InternalAppWatcher.install初始化时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// InternalAppWatcher.kt
init {
    val internalLeakCanary = try {
        val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
        leakCanaryListener.getDeclaredField("INSTANCE")
        .get(null)
    } catch (ignored: Throwable) {
        NoLeakCanary
    }
    @kotlin.Suppress("UNCHECKED_CAST")
    onAppWatcherInstalled = internalLeakCanary as (Application) -> Unit
}
fun install(application: Application) {
    checkMainThread()
    if (this::application.isInitialized) {
        return
    }
    SharkLog.logger = DefaultCanaryLog()
    InternalAppWatcher.application = application

    val configProvider = { AppWatcher.config }
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
    onAppWatcherInstalled(application)
}

通过调用install方法,对应的调用了onAppWatcherInstalled方法,进而调用了leakcanary.internal.InternalLeakCanary类的invoke方法来完成注册监听,其中该类通过反射拿到InternalLeakCanary.INSTANCE单例对象,这个类位于另一个包leakcanary-android-core下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// InternalLeakCanary.kt
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {
    ...
    override fun invoke(application: Application) {
        _application = application

        checkRunningInDebuggableBuild()
		// 注册监听
        AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
		// 创建AndroidHeapDumper对象,用于虚拟机dump hprof产生内存快照文件
        val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application))
		// 用来触发GC
        val gcTrigger = GcTrigger.Default

        val configProvider = { LeakCanary.config }
		// 创建子线程及对应looper
        val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
        handlerThread.start()
        val backgroundHandler = Handler(handlerThread.looper)

        // 创建HeapDumpTrigger对象,调用dumpHeap方法来创建hprof文件
        heapDumpTrigger = HeapDumpTrigger(
            application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
            configProvider
        )
        // 注册应用可见监听
        application.registerVisibilityListener { applicationVisible ->
                                                this.applicationVisible = applicationVisible
                                                heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
                                               }
        registerResumedActivityListener(application)
        addDynamicShortcut(application)

        disableDumpHeapInTests()
    }
    override fun onObjectRetained() {
        if (this::heapDumpTrigger.isInitialized) {
            heapDumpTrigger.onObjectRetained()
        }
    }
}

而前面执行回调OnObjectRetainedListener的onObjectRetained方法,就是在这里被调用的,它会调用HeapDumpTrigger.onObjectRetained()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// HeapDumpTrigger.kt
fun onObjectRetained() {
    scheduleRetainedObjectCheck(
        reason = "found new object retained",
        rescheduling = false
    )
}

private fun scheduleRetainedObjectCheck(
    reason: String,
    rescheduling: Boolean,
    delayMillis: Long = 0L
) {
    val checkCurrentlyScheduledAt = checkScheduledAt
    if (checkCurrentlyScheduledAt > 0) {
        val scheduledIn = checkCurrentlyScheduledAt - SystemClock.uptimeMillis()
        SharkLog.d { "Ignoring request to check for retained objects ($reason), already scheduled in ${scheduledIn}ms" }
        return
    } else {
        val verb = if (rescheduling) "Rescheduling" else "Scheduling"
        val delay = if (delayMillis > 0) " in ${delayMillis}ms" else ""
        SharkLog.d { "$verb check for retained objects${delay} because $reason" }
    }
    checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
    backgroundHandler.postDelayed({
        checkScheduledAt = 0
        checkRetainedObjects(reason)
    }, delayMillis)
}

private fun checkRetainedObjects(reason: String) {
    val config = configProvider()
    // A tick will be rescheduled when this is turned back on.
    if (!config.dumpHeap) {
        SharkLog.d { "Ignoring check for retained objects scheduled because $reason: LeakCanary.Config.dumpHeap is false" }
        return
    }
	// 记录未回收对象的数量
    var retainedReferenceCount = objectWatcher.retainedObjectCount

    if (retainedReferenceCount > 0) {
        // 主动触发GC
        gcTrigger.runGc()
        // 重新更新未回收对象的数量
        retainedReferenceCount = objectWatcher.retainedObjectCount
    }
	// 如果未回收对象个数未达到阈值5个,就返回
    if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return

    if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
        onRetainInstanceListener.onEvent(DebuggerIsAttached)
        showRetainedCountNotification(
            objectCount = retainedReferenceCount,
            contentText = application.getString(
                R.string.leak_canary_notification_retained_debugger_attached
            )
        )
        scheduleRetainedObjectCheck(
            reason = "debugger is attached",
            rescheduling = true,
            delayMillis = WAIT_FOR_DEBUG_MILLIS
        )
        return
    }

    val now = SystemClock.uptimeMillis()
    val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
    if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
        onRetainInstanceListener.onEvent(DumpHappenedRecently)
        showRetainedCountNotification(
            objectCount = retainedReferenceCount,
            contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
        )
        // 60s内执行一次
        scheduleRetainedObjectCheck(
            reason = "previous heap dump was ${elapsedSinceLastDumpMillis}ms ago (< ${WAIT_BETWEEN_HEAP_DUMPS_MILLIS}ms)",
            rescheduling = true,
            delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
        )
        return
    }

    SharkLog.d { "Check for retained objects found $retainedReferenceCount objects, dumping the heap" }
    dismissRetainedCountNotification()
    // 获取内存快照
    dumpHeap(retainedReferenceCount, retry = true)
}

private fun dumpHeap(
    retainedReferenceCount: Int,
    retry: Boolean
  ) {
	...
    // 获取当前内存快照文件hprof
    val heapDumpFile = heapDumper.dumpHeap()
    // 获取失败处理
    if (heapDumpFile == null) {
      if (retry) {
        SharkLog.d { "Failed to dump heap, will retry in $WAIT_AFTER_DUMP_FAILED_MILLIS ms" }
        scheduleRetainedObjectCheck(
            reason = "failed to dump heap",
            rescheduling = true,
            delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS
        )
      } else {
        SharkLog.d { "Failed to dump heap, will not automatically retry" }
      }
      showRetainedCountNotification(
          objectCount = retainedReferenceCount,
          contentText = application.getString(
              R.string.leak_canary_notification_retained_dump_failed
          )
      )
      return
    }
    lastDisplayedRetainedObjectCount = 0
    lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
    // 清理注册的监听
    objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
    // 开启服务分析hprof文件,即解析生成报告
    HeapAnalyzerService.runAnalysis(application, heapDumpFile)
  }

经过一系列调用,最后会执行到checkRetainedObjects方法,在该方法中先记录未回收对象的数量,然后主动GC一次,并更新该数量,如果未超过阈值5个就返回,否则就每隔60s执行一次,并通过dumpHeap获取内存快照。该方法内会调用AndroidHeapDumper的dumpHeap方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// AndroidHeapDumper.kt
override fun dumpHeap(): File? {
    val heapDumpFile = leakDirectoryProvider.newHeapDumpFile() ?: return null

    val waitingForToast = FutureResult<Toast?>()
    showToast(waitingForToast)

    if (!waitingForToast.wait(5, SECONDS)) {
        SharkLog.d { "Did not dump heap, too much time waiting for Toast." }
        return null
    }

    val notificationManager =
    context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    if (Notifications.canShowNotification) {
        val dumpingHeap = context.getString(R.string.leak_canary_notification_dumping)
        val builder = Notification.Builder(context)
        .setContentTitle(dumpingHeap)
        val notification = Notifications.buildNotification(context, builder, LEAKCANARY_LOW)
        notificationManager.notify(R.id.leak_canary_notification_dumping_heap, notification)
    }

    val toast = waitingForToast.get()

    return try {
        // 写入数据
        Debug.dumpHprofData(heapDumpFile.absolutePath)
        if (heapDumpFile.length() == 0L) {
            SharkLog.d { "Dumped heap file is 0 byte length" }
            null
        } else {
            heapDumpFile
        }
    } catch (e: Exception) {
        SharkLog.d(e) { "Could not dump heap" }
        // Abort heap dump
        null
    } finally {
        cancelToast(toast)
        notificationManager.cancel(R.id.leak_canary_notification_dumping_heap)
    }
}

在该方法内会调用leakDirectoryProvider的newHeapDumpFile方法生成hprof文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// LeakDirectoryProvider.kt
fun newHeapDumpFile(): File? {
    cleanupOldHeapDumps()

    var storageDirectory = externalStorageDirectory()
    if (!directoryWritableAfterMkdirs(storageDirectory)) {
        if (!hasStoragePermission()) {
            if (requestExternalStoragePermission()) {
                SharkLog.d { "WRITE_EXTERNAL_STORAGE permission not granted, requesting" }
                requestWritePermissionNotification()
            } else {
                SharkLog.d { "WRITE_EXTERNAL_STORAGE permission not granted, ignoring" }
            }
        } else {
            val state = Environment.getExternalStorageState()
            if (Environment.MEDIA_MOUNTED != state) {
                SharkLog.d { "External storage not mounted, state: $state" }
            } else {
                SharkLog.d {
                    "Could not create heap dump directory in external storage: [${storageDirectory.absolutePath}]"
                }
            }
        }
        // Fallback to app storage.
        storageDirectory = appStorageDirectory()
        if (!directoryWritableAfterMkdirs(storageDirectory)) {
            SharkLog.d {
                "Could not create heap dump directory in app storage: [${storageDirectory.absolutePath}]"
            }
            return null
        }
    }

    val fileName = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS'.hprof'", Locale.US).format(Date())
    // 创建hprof文件
    return File(storageDirectory, fileName)
}

紧接着我们看下hprof文件解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// HeapAnalyzerService.kt
internal class HeapAnalyzerService : ForegroundService(
    HeapAnalyzerService::class.java.simpleName,
    R.string.leak_canary_notification_analysing,
    R.id.leak_canary_notification_analyzing_heap
), OnAnalysisProgressListener {

    override fun onHandleIntentInForeground(intent: Intent?) {
        if (intent == null || !intent.hasExtra(HEAPDUMP_FILE_EXTRA)) {
            SharkLog.d { "HeapAnalyzerService received a null or empty intent, ignoring." }
            return
        }

        // Since we're running in the main process we should be careful not to impact it.
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
        // 获取到hprof文件
        val heapDumpFile = intent.getSerializableExtra(HEAPDUMP_FILE_EXTRA) as File

        val config = LeakCanary.config
        val heapAnalysis = if (heapDumpFile.exists()) {
            // 解析hprof文件
            analyzeHeap(heapDumpFile, config)
        } else {
            missingFileFailure(heapDumpFile)
        }
        onAnalysisProgress(REPORTING_HEAP_ANALYSIS)
        // 解析完成回调
        config.onHeapAnalyzedListener.onHeapAnalyzed(heapAnalysis)
    }

    private fun analyzeHeap(
        heapDumpFile: File,
        config: Config
    ): HeapAnalysis {
        val heapAnalyzer = HeapAnalyzer(this)

        val proguardMappingReader = try {
            ProguardMappingReader(assets.open(PROGUARD_MAPPING_FILE_NAME))
        } catch (e: IOException) {
            null
        }
        return heapAnalyzer.analyze(
            heapDumpFile = heapDumpFile,
            leakingObjectFinder = config.leakingObjectFinder,
            referenceMatchers = config.referenceMatchers,
            computeRetainedHeapSize = config.computeRetainedHeapSize,
            objectInspectors = config.objectInspectors,
            metadataExtractor = config.metadataExtractor,
            proguardMapping = proguardMappingReader?.readProguardMapping()
        )
    }

    private fun missingFileFailure(heapDumpFile: File): HeapAnalysisFailure {
        val deletedReason = LeakDirectoryProvider.hprofDeleteReason(heapDumpFile)
        val exception = IllegalStateException(
            "Hprof file $heapDumpFile missing, deleted because: $deletedReason"
        )
        return HeapAnalysisFailure(
            heapDumpFile = heapDumpFile,
            createdAtTimeMillis = System.currentTimeMillis(),
            analysisDurationMillis = 0,
            exception = HeapAnalysisException(exception)
        )
    }

    override fun onAnalysisProgress(step: OnAnalysisProgressListener.Step) {
        val percent =
        (100f * step.ordinal / OnAnalysisProgressListener.Step.values().size).toInt()
        SharkLog.d { "Analysis in progress, working on: ${step.name}" }
        val lowercase = step.name.replace("_", " ")
        .toLowerCase(Locale.US)
        val message = lowercase.substring(0, 1).toUpperCase(Locale.US) + lowercase.substring(1)
        showForegroundNotification(100, percent, false, message)
    }

    companion object {
        private const val HEAPDUMP_FILE_EXTRA = "HEAPDUMP_FILE_EXTRA"
        private const val PROGUARD_MAPPING_FILE_NAME = "leakCanaryObfuscationMapping.txt"

        fun runAnalysis(
            context: Context,
            heapDumpFile: File
        ) {
            val intent = Intent(context, HeapAnalyzerService::class.java)
            intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)
            startForegroundService(context, intent)
        }

        private fun startForegroundService(
            context: Context,
            intent: Intent
        ) {
            if (SDK_INT >= 26) {
                context.startForegroundService(intent)
            } else {
                // Pre-O behavior.
                context.startService(intent)
            }
        }
    }
}

它启动了一个前台服务,该服务继承自ForegroundService,而ForegroundService又继承自IntentService:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// ForegroundService.kt
internal abstract class ForegroundService(
  name: String,
  private val notificationContentTitleResId: Int,
  private val notificationId: Int
) : IntentService(name) {

  override fun onCreate() {
    super.onCreate()
    showForegroundNotification(
        max = 100, progress = 0, indeterminate = true,
        contentText = getString(R.string.leak_canary_notification_foreground_text)
    )
  }

  protected fun showForegroundNotification(
    max: Int,
    progress: Int,
    indeterminate: Boolean,
    contentText: String
  ) {
    val builder = Notification.Builder(this)
        .setContentTitle(getString(notificationContentTitleResId))
        .setContentText(contentText)
        .setProgress(max, progress, indeterminate)
    val notification =
      Notifications.buildNotification(this, builder, LEAKCANARY_LOW)
    startForeground(notificationId, notification)
  }

  override fun onHandleIntent(intent: Intent?) {
    onHandleIntentInForeground(intent)
  }

  protected abstract fun onHandleIntentInForeground(intent: Intent?)

  override fun onDestroy() {
    super.onDestroy()
    stopForeground(true)
  }

  override fun onBind(intent: Intent): IBinder? {
    return null
  }
}

IntentService内部会初始化一个HandlerThread,即带有looper的线程,在服务启动时,会发送一个消息到与该线程关联的handler,并调用onHandleIntent方法,所以该方法也执行在子线程,进而又调用到HeapAnalyzerService的onHandleIntentInForeground方法,执行analyzeHeap方法解析文件,在该方法内部创建一个HeapAnalyzer对象,通过调用其analyze方法完成解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// HeapAnalyzer.kt
fun analyze(
    heapDumpFile: File,
    leakingObjectFinder: LeakingObjectFinder,
    referenceMatchers: List<ReferenceMatcher> = emptyList(),
    computeRetainedHeapSize: Boolean = false,
    objectInspectors: List<ObjectInspector> = emptyList(),
    metadataExtractor: MetadataExtractor = MetadataExtractor.NO_OP,
    proguardMapping: ProguardMapping? = null
): HeapAnalysis {
    val analysisStartNanoTime = System.nanoTime()

    if (!heapDumpFile.exists()) {
        val exception = IllegalArgumentException("File does not exist: $heapDumpFile")
        return HeapAnalysisFailure(
            heapDumpFile, System.currentTimeMillis(), since(analysisStartNanoTime),
            HeapAnalysisException(exception)
        )
    }

    return try {
        listener.onAnalysisProgress(PARSING_HEAP_DUMP)
        Hprof.open(heapDumpFile)
        .use { hprof ->
              // 从文件中解析获取对象关系图结构graph并获取图中的所有GC roots根节点
              val graph = HprofHeapGraph.indexHprof(hprof, proguardMapping)
              val helpers =
              FindLeakInput(graph, referenceMatchers, computeRetainedHeapSize, objectInspectors)
              // 查找内存泄漏对象
              helpers.analyzeGraph(
                  metadataExtractor, leakingObjectFinder, heapDumpFile, analysisStartNanoTime
              )
             }
    } catch (exception: Throwable) {
        HeapAnalysisFailure(
            heapDumpFile, System.currentTimeMillis(), since(analysisStartNanoTime),
            HeapAnalysisException(exception)
        )
    }
}

private fun FindLeakInput.analyzeGraph(
    metadataExtractor: MetadataExtractor,
    leakingObjectFinder: LeakingObjectFinder,
    heapDumpFile: File,
    analysisStartNanoTime: Long
): HeapAnalysisSuccess {
    listener.onAnalysisProgress(EXTRACTING_METADATA)
    val metadata = metadataExtractor.extractMetadata(graph)

    listener.onAnalysisProgress(FINDING_RETAINED_OBJECTS)
    // 通过过滤graph中的KeyedWeakReference类型对象来找到对应的内存泄漏对象
    val leakingObjectIds = leakingObjectFinder.findLeakingObjectIds(graph)

    val (applicationLeaks, libraryLeaks) = findLeaks(leakingObjectIds)

    return HeapAnalysisSuccess(
        heapDumpFile = heapDumpFile,
        createdAtTimeMillis = System.currentTimeMillis(),
        analysisDurationMillis = since(analysisStartNanoTime),
        metadata = metadata,
        applicationLeaks = applicationLeaks,
        libraryLeaks = libraryLeaks
    )
}

在该方法实现了解析hprof文件找到内存泄漏对象,并计算对象到GC roots的最短路径,输出报告。

上面通过调用KeyedWeakReferenceFinder的findLeakingObjectIds方法过滤出泄露对象,再通过findLeaks方法计算到GC Roots的路径:

1
2
3
4
5
6
7
8
9
10
11
// HeapAnalyzer.kt
private fun FindLeakInput.findLeaks(leakingObjectIds: Set<Long>): Pair<List<ApplicationLeak>, List<LibraryLeak>> {
    val pathFinder = PathFinder(graph, listener, referenceMatchers)
    // 计算并获取目标对象到GC roots的最短路径
    val pathFindingResults =
    pathFinder.findPathsFromGcRoots(leakingObjectIds, computeRetainedHeapSize)

    SharkLog.d { "Found ${leakingObjectIds.size} retained objects" }
	// 将这些内存泄漏对象的最短路径合并成树结构返回
    return buildLeakTraces(pathFindingResults)
}

最后通过可视化界面将hprof分析结果HeapAnalysisSuccess展示出来。

而解析完成的回调方法是onHeapAnalyzedListener.onHeapAnalyzed,它的默认实现类是DefaultOnHeapAnalyzedListener,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// DefaultOnHeapAnalyzedListener.kt
override fun onHeapAnalyzed(heapAnalysis: HeapAnalysis) {
    SharkLog.d { "$heapAnalysis" }

    val id = LeaksDbHelper(application).writableDatabase.use { db ->
        HeapAnalysisTable.insert(db, heapAnalysis)
                                                             }

    val (contentTitle, screenToShow) = when (heapAnalysis) {
        is HeapAnalysisFailure -> application.getString(
            R.string.leak_canary_analysis_failed
        ) to HeapAnalysisFailureScreen(id)
            is HeapAnalysisSuccess -> {
            val retainedObjectCount = heapAnalysis.allLeaks.sumBy { it.leakTraces.size }
            val leakTypeCount = heapAnalysis.applicationLeaks.size + heapAnalysis.libraryLeaks.size
                application.getString(
                R.string.leak_canary_analysis_success_notification, retainedObjectCount, leakTypeCount
            ) to HeapDumpScreen(id)
        }
    }

    if (InternalLeakCanary.formFactor == TV) {
        // toast展示
        showToast(heapAnalysis)
            printIntentInfo()
    } else {
        // 通知展示
        showNotification(screenToShow, contentTitle)
    }
}

总结

首先注册监听Activity的生命周期onDestory,然后在onDestory方法中,通过ObjectWatcher来watch该Activity,在该方法中,创建带有标识的KeyedWeakReference对象,并关联ReferenceQueue,并存在监控集合Map中,延时5秒来检查目标对象是否回收,如果有未回收的对象则主动触发GC,并每60s检查一次,满足5个阈值,就开始dump heap获取内存快照文件,并解析该文件,找到内存泄露对象,然后计算该对象到GC Roots的最短路径,合并所有路径为树结构返回,最后以可视化的方式展示界面。

本文由作者按照 CC BY 4.0 进行授权

Android三方开源库之BlockCanary源码分析

Android三方开源库之Retrofit源码分析