LeakCanary 核心原理
LeakCanary 的内存泄漏检测基于 弱引用 + 引用队列 的机制。主要分为以下几个阶段:
- 对象监控阶段
- 弱引用机制
- 引用队列检测
- 泄漏判断机制
- 堆转储与分析
- 引用链分析
1. 对象监控阶段
核心组件:
- ObjectWatcher - 对象观察器
- KeyedWeakReference - 带键的弱引用
- ReferenceQueue - 引用队列
ObjectWatcher
LeakCanary 中共有 4 个主要的 Watcher,它们分别是:
- ActivityWatcher
- FragmentAndViewModelWatcher
- ServiceWatcher
- RootViewWatcher
每个 Watcher 都实现了 InstallableWatcher 接口,具有 install() 和 uninstall() 方法,可以灵活地启用或禁用特定的监控组件。
interface InstallableWatcher {
fun install()
fun uninstall()
}
ActivityWatcher在install时,监听了 Application.ActivityLifecycleCallbacks
class ActivityWatcher(
private val application: Application,
private val deletableObjectReporter: DeletableObjectReporter
) : InstallableWatcher {
// Kept for backward compatibility.
constructor(
application: Application,
reachabilityWatcher: ReachabilityWatcher
) : this(application, reachabilityWatcher.asDeletableObjectReporter())
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
deletableObjectReporter.expectDeletionFor(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
override fun install() {
// 监听 lifecycleCallbacks
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
// 取消监听
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
}
其它3个 FragmentAndViewModelWatcher、ServiceWatcher、RootViewWatcher 原理类似:
- 监听 Fragment 的 onDestroy() 和 onDestroyView() 回调
- 监听 AndroidX ViewModel 的 onCleared() 回调
- 监听 Service 的 onDestroy() 回调
- 监听 View 的 onDetachedFromWindow() 回调
4个Watcher 和 ObjectWatcher
可以看到在onDestroy 里面并没有直接调用到 ObjectWatcher
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
// ObjectWatcher 实现了 ReachabilityWatcher 接口
deletableObjectReporter.expectDeletionFor(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
而是使用了一种适配器模式,通过接口来调用, 这样可以让其他 Watcher 不直接依赖 ObjectWatcher,而是依赖更抽象的 DeletableObjectReporter 接口。
fun interface ReachabilityWatcher {
fun expectWeaklyReachable(
watchedObject: Any,
description: String
)
fun asDeletableObjectReporter(): DeletableObjectReporter =
DeletableObjectReporter { target, reason ->
expectWeaklyReachable(target, reason)
}
}
为什么要使用适配器模式?
这样可以给 ActivityWatcher 传入不同的 DeletableObjectReporter。例如默认的 ObjectWatcher、带延迟的包装器、过滤的包装器等等。
ActivityWatcher(application, defaultReporter)
ActivityWatcher(application, delayedReporter)
ActivityWatcher(application, filteredReporter)
这其实也更符合软件设计的 依赖倒置原则
原则内容:
- 高层模块不应依赖于低层模块,二者都应依赖于抽象
- 抽象不应依赖于细节,细节应依赖于抽象
LeakCanary 中的应用:
// 高层模块(ActivityWatcher)依赖抽象接口
class ActivityWatcher(
private val application: Application,
private val deletableObjectReporter: DeletableObjectReporter // 依赖抽象
) : InstallableWatcher
// 低层模块(ObjectWatcher)实现抽象接口
class ObjectWatcher : ReachabilityWatcher, RetainedObjectTracker {
// 实现细节
}
ObjectWatcher 的核心实现
class ObjectWatcher private constructor(
private val checkRetainedExecutor: Executor,
private val clock: Clock,
private val isEnabled: () -> Boolean,
private val onObjectRetainedListeners: MutableSet<OnObjectRetainedListener> = CopyOnWriteArraySet(),
private val retainedObjectTracker: ReferenceQueueRetainedObjectTracker = ReferenceQueueRetainedObjectTracker(
{ clock.uptimeMillis().milliseconds }) {
onObjectRetainedListeners.forEach { it.onObjectRetained() }
},
// 🌟使用委托模式 (by retainedObjectTracker) 将核心逻辑委托给 ReferenceQueueRetainedObjectTracker
) : RetainedObjectTracker by retainedObjectTracker, ReachabilityWatcher {
constructor(
clock: Clock,
checkRetainedExecutor: Executor,
/**
* Calls to [] will be ignored when [isEnabled] returns false
*/
isEnabled: () -> Boolean = { true },
) : this(
checkRetainedExecutor, clock, isEnabled
)
/**
* Returns true if there are watched objects that aren't weakly reachable, even
* if they haven't been watched for long enough to be considered retained.
*/
val hasWatchedObjects: Boolean
get() = hasTrackedObjects
/**
* Returns the objects that are currently considered retained. Calling this method will
* end up creating local references to the objects, preventing them from becoming weakly
* reachable, and creating a leak.
*/
val retainedObjects: List<Any>
get() = retainedObjectTracker.retainedWeakReferences.mapNotNull { it.getAndLeakReferent() }
fun addOnObjectRetainedListener(listener: OnObjectRetainedListener) {
onObjectRetainedListeners.add(listener)
}
fun removeOnObjectRetainedListener(listener: OnObjectRetainedListener) {
onObjectRetainedListeners.remove(listener)
}
/**
* Identical to [watch] with an empty string reference name.
*/
fun watch(watchedObject: Any) {
expectWeaklyReachable(watchedObject, "unknown: reason not provided")
}
fun watch(
watchedObject: Any,
description: String
) {
expectWeaklyReachable(watchedObject, description)
}
// 核心方法
override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
// 1. 创建触发器
val retainTrigger =
retainedObjectTracker.expectDeletionOnTriggerFor(watchedObject, description)
// 2. 延迟检查
checkRetainedExecutor.execute {
retainTrigger.markRetainedIfStronglyReachable()
}
}
/**
* Clears all [KeyedWeakReference] that were created before [heapDumpUptimeMillis] (based on
* [clock] [Clock.uptimeMillis])
*/
fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) {
clearObjectsTrackedBefore(heapDumpUptimeMillis.milliseconds)
}
/**
* Clears all [KeyedWeakReference]
*/
fun clearWatchedObjects() {
clearAllObjectsTracked()
}
}
可以看到关键方法在于 expectDeletionOnTriggerFor 这个方法,刚才说到核心逻辑其实是委托给了 ReferenceQueueRetainedObjectTracker,我们可以直接查看 ReferenceQueueRetainedObjectTracker 的 expectDeletionOnTriggerFor 方法
class ReferenceQueueRetainedObjectTracker constructor(
private val clock: UptimeClock,
private val onObjectRetainedListener: OnObjectRetainedListener
) : RetainedObjectTracker, TriggeredDeletableObjectReporter {
private val watchedObjects: MutableMap<String, KeyedWeakReference> = ConcurrentHashMap()
private val queue = ReferenceQueue<Any>() // 引用队列
val trackedWeakReferences: List<KeyedWeakReference> // 被监控的弱引用列表
val retainedWeakReferences: List<KeyedWeakReference> // 被标记为泄漏的弱引用列表
override val hasRetainedObjects: Boolean
override val retainedObjectCount: Int
override val hasTrackedObjects: Boolean
override val trackedObjectCount: Int
// 开始监控一个对象,期望它能被回收
override fun expectDeletionOnTriggerFor(
target: Any,
reason: String
): RetainTrigger {
// 1. 清理已被回收的对象
removeWeaklyReachableObjects()
// 2. 生成唯一标识符
val key = UUID.randomUUID()
.toString()
val watchUptime = clock.uptime()
// 3. 创建弱引用包装
val reference =
KeyedWeakReference(target, key, reason, watchUptime.inWholeMilliseconds, queue)
// 4. 存储到监控列表
watchedObjects[key] = reference
// 5. 返回触发器
return object : RetainTrigger {
override val isStronglyReachable: Boolean
override val isRetained: Boolean
override fun markRetainedIfStronglyReachable() {
moveToRetained(key)
}
}
}
override fun clearObjectsTrackedBefore(uptime: Duration) {
val weakRefsToRemove =
watchedObjects.filter { it.value.watchUptimeMillis <= uptime.inWholeMilliseconds }
weakRefsToRemove.values.forEach { it.clear() }
watchedObjects.keys.removeAll(weakRefsToRemove.keys)
}
override fun clearAllObjectsTracked() {
watchedObjects.values.forEach { it.clear() }
watchedObjects.clear()
}
// 将指定对象标记为泄漏
private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptime().inWholeMilliseconds
onObjectRetainedListener.onObjectRetained()
}
}
// removeWeaklyReachableObjects()
private fun removeWeaklyReachableObjects() {
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
}
整体流程图
Activity.onDestroy()
↓
ActivityWatcher.onActivityDestroyed()
↓
deletableObjectReporter.expectDeletionFor()
↓
ObjectWatcher.expectWeaklyReachable()
↓
ReferenceQueueRetainedObjectTracker.expectDeletionOnTriggerFor()
↓
创建 KeyedWeakReference 并存储到 watchedObjects
↓
延迟 5 秒执行检查
↓
RetainTrigger.markRetainedIfStronglyReachable()
↓
moveToRetained()
↓
检查对象是否仍在 watchedObjects 中
↓
如果存在:标记为泄漏,通知监听器
如果不存在:已被正常回收