LeakCanary 原理完整剖析:从初始化到堆分析

LeakCanary 原理完整剖析:从初始化到堆分析

LeakCanary 的核心逻辑极其简单------在对象"该死"的那一刻标记它,过一会儿看看它死了没有------但围绕这个核心,做了大量的边界处理、策略优化和工具链建设。


一、初始化:LeakCanary 是怎么自己启动的

你只要在 build.gradle 里加了依赖,LeakCanary 就会自动开始工作。靠的是 ContentProvider

Android 的规则:应用启动时,所有 Manifest 里的 ContentProvider 按优先级依次调用 onCreate()这个时机比 Application.onCreate() 还要早

LeakCanary 在自己的 Manifest 里声明了一个 ContentProvider:

xml 复制代码
<provider
    android:name="leakcanary.internal.AppWatcherInstaller"
    android:authorities="${applicationId}.leakcanary-installer"
    android:exported="false" />

这个 Provider 的 onCreate() 只干了一件事:

kotlin 复制代码
// AppWatcherInstaller.kt --- ContentProvider 入口
class AppWatcherInstaller : ContentProvider() {
    override fun onCreate(): Boolean {
        AppWatcher.manualInstall(context as Application)
        return true
    }
}

manualInstall 内部做了这些事:

kotlin 复制代码
// AppWatcher.kt
object AppWatcher {

    // ★ 全局唯一的 ObjectWatcher
    // checkRetainedExecutor 是一个延迟 5 秒执行的线程
    val objectWatcher = ObjectWatcher(
        clock = UptimeClock(),
        checkRetainedExecutor = Executor { command ->
            Handler(HandlerThread("LeakCanary-OW").apply { start() }.looper)
                .postDelayed(command, TimeUnit.SECONDS.toMillis(5))
        }
    )

    fun manualInstall(application: Application) {
        // ① 注册 Activity 监听
        ActivityWatcher(application, objectWatcher).install()
        // ② 注册 Fragment + ViewModel 监听
        FragmentAndViewModelWatcher(objectWatcher).install(application)
        // ③ 注册 View 监听
        RootViewWatcher(objectWatcher).install(application)
        // ④ 启动 InternalLeakCanary(创建 HeapDumpTrigger 等)
        InternalLeakCanary(application)
    }
}

InternalLeakCanary 作为组织者登场:

kotlin 复制代码
// InternalLeakCanary.kt
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener {

    override fun invoke(application: Application) {
        _application = application

        // ★ 把自己注册到 ObjectWatcher,当有 retained 对象时触发后续流程
        AppWatcher.objectWatcher.addOnObjectRetainedListener(this)

        val gcTrigger = GcTrigger.inProcess()
        val configProvider = { LeakCanary.config }

        val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
        handlerThread.start()
        val backgroundHandler = Handler(handlerThread.looper)

        // ★ 创建 HeapDumpTrigger --- 负责 GC 确认 + dump
        heapDumpTrigger = HeapDumpTrigger(
            application, backgroundHandler,
            AppWatcher.objectWatcher, gcTrigger, configProvider
        )

        // ★ 监听前后台切换(前台才 dump,后台延迟)
        application.registerVisibilityListener { visible ->
            applicationVisible = visible
            heapDumpTrigger.onApplicationVisibilityChanged(visible)
        }

        registerResumedActivityListener(application)
    }

    // ★ 当 ObjectWatcher 发现 retained 对象时,此方法被回调
    override fun onObjectRetained() {
        heapDumpTrigger.onObjectRetained()
    }
}

初始化完整链路:

scss 复制代码
ContentProvider.onCreate()
  → AppWatcher.manualInstall()
    → ActivityWatcher.install()           注册 ActivityLifecycleCallbacks
    → FragmentAndViewModelWatcher.install() 注册 Fragment + ViewModel 监听
    → RootViewWatcher.install()           注册 OnAttachStateChangeListener
    → InternalLeakCanary.invoke()
      → 创建 HeapDumpTrigger
      → 注册 OnObjectRetainedListener
      → 注册前后台监听

二、监听入口:LeakCanary 怎么感知"对象该死了"

Activity

kotlin 复制代码
// ActivityWatcher.kt
class ActivityWatcher(
    private val application: Application,
    private val objectWatcher: ObjectWatcher
) : ActivityLifecycleCallbacks {

    fun install() {
        // ★ 注册全局 Activity 生命周期监听
        application.registerActivityLifecycleCallbacks(this)
    }

    override fun onActivityDestroyed(activity: Activity) {
        // ★ 每个 Activity 的 onDestroy 都会走到这里
        objectWatcher.watch(
            watchedObject = activity,
            description = "Activity ${activity::class.java.name} received onDestroy()"
        )
    }
}

Fragment(含嵌套 Fragment)

Fragment 的生命周期依附于 Activity。LeakCanary 在 Activity 创建的时机拿到它的 FragmentManager:

kotlin 复制代码
// FragmentAndViewModelWatcher.kt
class FragmentAndViewModelWatcher(
    private val objectWatcher: ObjectWatcher
) {
    fun install(application: Application) {
        // 监听 Activity 创建
        application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                if (activity is FragmentActivity) {
                    val fm = activity.supportFragmentManager

                    // ★★ 第一步:为 Activity 本身安装 ViewModel 监听 ★★
                    // Activity(FragmentActivity)也是 ViewModelStoreOwner
                    // 它有自己的 ViewModelStore,也要被监视
                    installViewModelClearedWatcher(activity, activity.viewModelStore)

                    // ★ 第二步:注册 Fragment 生命周期监听
                    // 第三个参数 recursive = true
                    // → 自动递归监听所有 child FragmentManager
                    // → 不管 Fragment 嵌套多少层都能覆盖
                    fm.registerFragmentLifecycleCallbacks(
                        object : FragmentManager.FragmentLifecycleCallbacks() {

                            override fun onFragmentCreated(
                                fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?
                            ) {
                                // ★ 为每个 Fragment 安装 ViewModel 监听
                                // Fragment 也是 ViewModelStoreOwner
                                if (f is ViewModelStoreOwner) {
                                    installViewModelClearedWatcher(f, f.viewModelStore)
                                }
                            }

                            override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) {
                                objectWatcher.watch(f, "Fragment received onDestroy()")
                            }

                            override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
                                // View 和 Fragment 本身分开监视
                                f.view?.let {
                                    objectWatcher.watch(it, "Fragment view detached")
                                }
                            }
                        },
                        true // ★ recursive = true
                    )
                }
            }
        })
    }

    private fun installViewModelClearedWatcher(owner: ViewModelStoreOwner, store: ViewModelStore) {
        ViewModelProvider(owner, ...)
            .get(ViewModelClearedWatcher::class.java)
            .setViewModelStore(store)
    }
}

recursive = true 的工作方式:

java 复制代码
FragmentManager (Activity)
  ├── Fragment A
  │   ├── Fragment A1 (child)
  │   └── Fragment A2 (child)
  │       └── Fragment A2a (grandchild)
  └── Fragment B

recursive = true → A1、A2、A2a、B 的 onDestroy/viewDestroy 全部被监听
                 → 每个 Fragment 的 ViewModelStore 也都安装了 watcher

View

kotlin 复制代码
// RootViewWatcher.kt
class RootViewWatcher(
    private val objectWatcher: ObjectWatcher
) {
    fun install(application: Application) {
        application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                activity.window.decorView.addOnAttachStateChangeListener(
                    object : View.OnAttachStateChangeListener {
                        override fun onViewAttachedToWindow(v: View) {}
                        override fun onViewDetachedFromWindow(v: View) {
                            objectWatcher.watch(v, "View detached from window")
                        }
                    }
                )
            }
        })
    }
}

三、ViewModel 监听:最巧妙的设计

ViewModel 没有直接的"销毁回调"。它的清理发生在 ViewModelStore.clear() 里,而 ViewModelStore 的 map 是私有的。

LeakCanary 的设计:在每个 ViewModelStoreOwner(Activity/Fragment)创建时,用 ViewModelProvider 创建一个傀儡 ViewModel。 这个 ViewModel 的唯一使命是等自己的 onCleared() 被调用。

kotlin 复制代码
// ViewModelClearedWatcher.kt
class ViewModelClearedWatcher(
    private val objectWatcher: ObjectWatcher,
    private val viewModelStore: ViewModelStore
) : ViewModel() {

    // ★ ViewModelStore.clear() 遍历所有 ViewModel 挨个调 onCleared()
    // → 轮到 ViewModelClearedWatcher 时 onCleared() 被执行
    override fun onCleared() {
        // 反射拿到 ViewModelStore 的私有 map
        val map = getViewModelStoreMap(viewModelStore)

        for ((clazz, viewModel) in map) {
            if (viewModel !== this) {
                // 对每个 ViewModel 调 watch()
                objectWatcher.watch(
                    watchedObject = viewModel,
                    description = "${clazz.name} received onCleared()"
                )
            }
        }
    }

    // ★ 通过反射拿到 ViewModelStore 的私有 HashMap
    private fun getViewModelStoreMap(store: ViewModelStore): Map<Class<*>, ViewModel> {
        val field = ViewModelStore::class.java.getDeclaredField("map")
        field.isAccessible = true
        @Suppress("UNCHECKED_CAST")
        return field.get(store) as HashMap<String, ViewModel>
    }
}

ViewModel 检测时序:

scss 复制代码
Activity/Fragment 创建
  → ViewModelProvider(owner).get(ViewModelClearedWatcher)
  → Watcher 持有 owner 的 ViewModelStore 引用

Activity/Fragment 销毁
  → onDestroy → watch(activity/fragment)      ← Activity/Fragment 本身
  → ViewModelStore.clear()
    → 遍历所有 ViewModel,挨个调 onCleared()
      → ★ ViewModelClearedWatcher.onCleared()
        → 反射拿到 store 的 map
        → 对每个 ViewModel 调 watch()

5 秒后 GC 检查
  → 如果 ViewModel 实例还在 → 判定泄漏

四、KeyedWeakReference:自定义的弱引用

kotlin 复制代码
// KeyedWeakReference.kt
class KeyedWeakReference(
    referent: Any,                // 被监视的对象
    val key: String,              // 唯一标识(UUID),用于堆分析时定位
    val name: String,             // 描述信息
    val watchUptimeMillis: Long,  // 监视开始时间戳
    referenceQueue: ReferenceQueue<Any>  // ★ 关联的 ReferenceQueue
) : WeakReference<Any>(referent, referenceQueue) {

    @Volatile
    var retainedUptimeMillis: Long = 0

    val retained: Boolean get() = retainedUptimeMillis != 0L
}

五、ReferenceQueue:判断对象是否被回收的关键

ReferenceQueue 是 Java 标准库提供的类。当一个 WeakReference 引用的对象被 GC 回收后,这个 WeakReference 会被 JVM 自动入队到它关联的 ReferenceQueue。

java 复制代码
ReferenceQueue<Object> queue = new ReferenceQueue<>();
Object object = new Object();
WeakReference<Object> wr = new WeakReference<>(object, queue);

// wr.get() → object(存活)
// queue.poll() → null

object = null;
System.gc();  // 假设 GC 执行了

// GC 之后:
// wr.get() → null
// queue.poll() → wr(WeakReference 被自动放入 queue!)

所以只需 queue.poll() 判断返回的是 null 还是 ref:

kotlin 复制代码
// poll() 是非阻塞的
val ref = queue.poll()
if (ref != null) {
    // 对象已被 GC 回收
} else {
    // 对象还活着
}

ReferenceQueue 相比直接调 wr.get() 的优势

直接调 wr.get() 有副作用------get() 返回一个强引用,调用它本身就会阻止对象被回收 。ReferenceQueue 是被动接收 JVM 通知,没有这个副作用


六、ObjectWatcher:watch() 核心检测逻辑

kotlin 复制代码
// ObjectWatcher.kt
fun watch(watchedObject: Any, description: String) {
    // ★ 委托给 ReferenceQueueRetainedObjectTracker
    expectWeaklyReachable(watchedObject, description)
}

override fun expectWeaklyReachable(watchedObject: Any, description: String) {
    if (!isEnabled()) return

    // ① 创建弱引用并存入 map(这个方法内部做了下面几步)
    val retainTrigger =
        retainedObjectTracker.expectDeletionOnTriggerFor(watchedObject, description)
    // expectDeletionOnTriggerFor 内部:
    //   1. removeWeaklyReachableObjects()  ← 先清理已被回收的
    //   2. 创建 KeyedWeakReference(object, key, queue)
    //   3. 存入 watchedObjects[key] = ref
    //   4. 返回 RetainTrigger

    // ② 5 秒后检查(不做 GC!)
    checkRetainedExecutor.execute {
        retainTrigger.markRetainedIfStronglyReachable()
    }
}
kotlin 复制代码
// ReferenceQueueRetainedObjectTracker.expectDeletionOnTriggerFor()
override fun expectDeletionOnTriggerFor(target: Any, reason: String): RetainTrigger {
    // ① 先清理已被回收的
    removeWeaklyReachableObjects()

    val key = UUID.randomUUID().toString()

    // ② 创建 KeyedWeakReference,关联到 ReferenceQueue
    val reference = KeyedWeakReference(target, key, reason, clock.uptime(), queue)

    // ③ 存入 map
    watchedObjects[key] = reference

    // ④ 返回 RetainTrigger
    return object : RetainTrigger {
        override fun markRetainedIfStronglyReachable() {
            // ★ 5 秒后被调用
            removeWeaklyReachableObjects()

            val retainedRef = watchedObjects[key]
            if (retainedRef != null) {
                // 对象还在 map 里 → 没被回收 → 标记泄漏
                retainedRef.retainedUptimeMillis = clock.uptime().inWholeMilliseconds
                onObjectRetainedListener.onObjectRetained()
            }
        }
    }
}

// ★ 轮询 ReferenceQueue,清理已被回收的
private fun removeWeaklyReachableObjects() {
    var ref: KeyedWeakReference?
    do {
        ref = queue.poll() as? KeyedWeakReference?
        if (ref != null) {
            watchedObjects.remove(ref.key)
        }
    } while (ref != null)
}

七、GcTrigger:GC 确认

kotlin 复制代码
// GcTrigger.kt
class InProcessGcTrigger : GcTrigger {
    override fun runGc() {
        // ★ 尝试 5 次,每次等 100ms
        for (i in 0 until 5) {
            System.gc()
            Runtime.getRuntime().gc()
            Thread.sleep(100)
        }
    }
}

为什么 System.gc() 一次不够? 在 Android 上它只是"建议",ART 可以选择不执行。5 次触发能极大提高 GC 实际发生的概率。


八、HeapDumpTrigger:完整检查 + 退避策略

kotlin 复制代码
// HeapDumpTrigger.kt
class HeapDumpTrigger(...) {

    private var currentRetryDelay = RETRY_INITIAL_DELAY_MS  // 初始 2 秒

    fun onObjectRetained() {
        scheduleRetainedObjectCheck()
    }

    private fun scheduleRetainedObjectCheck(delayMs: Long = 0L) {
        backgroundHandler.postDelayed({ checkRetainedObjects() }, delayMs)
    }

    private fun checkRetainedObjects() {
        // ① 做 GC!×5 次!
        gcTrigger.runGc()

        // ② 清理 ReferenceQueue
        objectWatcher.removeWeaklyReachableReferences()

        val retainedCount = objectWatcher.retainedObjectCount
        if (retainedCount == 0) {
            // GC 后全回收了 → 虚惊一场
            currentRetryDelay = RETRY_INITIAL_DELAY_MS
            return
        }

        // ③ 检查 dump 条件
        when (iCanHasHeap()) {
            is Yup -> {
                currentRetryDelay = RETRY_INITIAL_DELAY_MS
                dumpHeap()
            }
            is Nope -> {
                // ★ 指数退避:2s → 4s → 8s → ... → 最大 120s
                scheduleRetainedObjectCheck(currentRetryDelay)
                currentRetryDelay = (currentRetryDelay * 2)
                    .coerceAtMost(RETRY_MAX_DELAY_MS)
            }
        }
    }

    private fun iCanHasHeap(): ICanHazHeap {
        if (!applicationVisible) return Nope("App 在后台")
        if (Debug.isDebuggerConnected()) return Nope("debugger 连接中")
        if (heapAnalysisInProgress) return Nope("上一个分析未完成")
        if (diskSpace < MIN_DISK_SPACE) return Nope("磁盘空间不足")
        return Yup("条件满足")
    }
}

九、Heap Dump:堆转储

kotlin 复制代码
// AndroidHeapDumper.kt
class AndroidHeapDumper : HeapDumper {
    override fun dumpHeap(): File? {
        val heapDumpFile = createTempFile("leakcanary_", ".hprof")
        return try {
            // ★ Android SDK 自带的堆转储 API
            // 调用时 ART 暂停当前进程,写入整个堆快照
            // 文件大小:几十到几百 MB
            Debug.dumpHprofData(heapDumpFile.absolutePath)
            if (heapDumpFile.length() > 0) heapDumpFile else null
        } catch (e: Exception) { null }
    }
}

十、shark 分析

kotlin 复制代码
fun analyzeHeap(heapDumpFile: File, retainedKeys: Set<String>): HeapAnalysis {
    // 1. 流式打开 hprof(Okio,不加载整个堆)
    val hprof = Hprof.open(heapDumpFile.inputStream())

    // 2. 扫描所有 KeyedWeakReference 实例
    val refClass = hprof.findClass("leakcanary.KeyedWeakReference")

    for (ref in refClass.instances) {
        // 3. 读取 key 字段,匹配 retainedKeys
        val key = ref["key"] as String
        if (key in retainedKeys) {
            // 4. 读取 referent → 泄漏对象
            val referent = ref["referent"] as HeapObject
            // 5. 反向遍历到 GC Root
            val leakTrace = findShortestPathToGcRoot(referent)
            leaks.add(LeakTrace(leakTrace))
        }
    }
    return HeapAnalysis(leaks)
}

十一、完整时序图

scss 复制代码
Application 启动
  │
  ▼
AppWatcherInstaller (ContentProvider.onCreate)
  │
  ▼
AppWatcher.manualInstall()
  ├─ ActivityWatcher → registerActivityLifecycleCallbacks()
  ├─ FragmentAndViewModelWatcher
  │   └─ onActivityCreated
  │       ├─ 注册 FragmentLifecycleCallbacks(recursive=true)
  │       └─ 为 Activity 安装 ViewModelClearedWatcher
  │           → onFragmentCreated 为每个 Fragment 也安装
  ├─ RootViewWatcher → OnAttachStateChangeListener
  └─ InternalLeakCanary
      ├─ 创建 HeapDumpTrigger
      └─ 注册 OnObjectRetainedListener
──────────────────────────────────────────
Activity.onDestroy() ─────────────────────
  │
  ├─ 直接:ObjectWatcher.watch(activity)
  │     → KeyedWeakReference(activity, key, queue)
  │     → 存入 watchedObjects
  │
  ├─ ViewModelStore.clear()
  │   → ViewModelClearedWatcher.onCleared()
  │     → 反射遍历 map → watch 每个 ViewModel
  │       → KeyedWeakReference(viewModel, key, queue)
  │       → 存入 watchedObjects
  │
  ├─ Fragment.onDestroy()
  │   ├─ watch(fragment)
  │   │   → KeyedWeakReference(fragment, key, queue)
  │   ├─ Fragment ViewModelStore.clear()
  │   │   → 同样的 ViewModelClearedWatcher 逻辑
  │   └─ child Fragment.onDestroy()
  │       → recursive=true 递归触发,同上
  │
  └─ Fragment.onFragmentViewDestroyed()
      → watch(view)
      → KeyedWeakReference(view, key, queue)
──────────────────────────────────────────
5 秒后 ────────────────────────────────────
  │
  ├─ markRetainedIfStronglyReachable()
  │   ├─ 不做 GC,只 poll queue
  │   ├─ queue 里有 → 对象已回收 → pass
  │   └─ queue 里没有 → 标记 retained
  │       → onObjectRetainedListener.onObjectRetained()
  │         → InternalLeakCanary
  │           → HeapDumpTrigger.onObjectRetained()
  │
  ▼
HeapDumpTrigger 阶段 ──────────────────────
  │
  ├─ checkRetainedObjects()
  │   ├─ gcTrigger.runGc() × 5 次
  │   ├─ 清理 queue
  │   │
  │   ├─ 全回收了 → 虚惊一场,return
  │   │
  │   └─ 还有残留 → canDumpHeap()?
  │       ├─ Nope → 指数退避重试
  │       └─ Yup → Debug.dumpHprofData()
  │           │
  │           ▼
  │       .hprof 文件(几十~几百 MB)
  │           │
  │           ▼
  │       shark 分析
  │         ├─ 扫描 KeyedWeakReference 实例
  │         ├─ 匹配 retainedKeys
  │         ├─ 通过 referent 找到泄漏对象
  │         ├─ 反向遍历到 GC Root
  │         └─ 输出 LeakTrace
  │           │
  │           ▼
  │       通知栏 + LeakActivity 展示

十二、LeakCanary 不能检测的内存泄漏

检测原理决定了边界

LeakCanary 的检测逻辑是:T0 时刻标记"这个对象该死了",T1 时刻验证它是不是真死了。 这要求必须有明确的 T0。

有 T0 → 能检测:

对象 T0 怎么拿到的
Activity onDestroy() ActivityLifecycleCallbacks
Fragment onDestroy() FragmentLifecycleCallbacks(recursive=true)
View onDetachedFromWindow OnAttachStateChangeListener
ViewModel onCleared() 傀儡 ViewModel 反射遍历 store
自定义对象 你手动调 watch() 你自己代码调

没有 T0 → 不能检测:

泄漏类型 场景举例 为什么 LeakCanary 抓不到
增长型集合泄漏 static HashMap<String, Object> 每次请求 put 数据但不 remove 没有"这个 entry 该死"的时刻。LeakCanary 不知道哪个 entry 应该被移除
Listener 累积泄漏 单例调用 addListener(activity) 但从不 removeListener Listener 没有收到"你该死"的通知。Activity 本身可能已被 GC,但 listener 引用链还在
线程池任务堆积 Executors.newCachedThreadPool() 不断 submit 任务 线程没有"该死"的回调
Handler Message 积压 Handler 发出延迟消息,Activity 销毁时未 removeCallbacks Message 里的 target(Handler)持有 Activity 引用。LeakCanary 能检测到 Activity 泄漏,但不会告诉你"是因为这个未移除的 Message"
未关闭资源 Cursor、InputStream、Socket 未关闭 资源泄漏 ≠ 对象泄漏。Cursor 对象本身可能已被 GC,但底层连接没释放
Bitmap 分配过多 列表滑动时不断创建 Bitmap 不复用 这是分配问题,不是泄漏。Bitmap 有正常生命周期,只是每次分配新的
单例缓存无限增大 object Cache { val data = mutableListOf<T>() } 只增不减 所有元素都可达,LeakCanary 没有 T0 判断"哪个元素该被移除"

具体代码示例

完全抓不到的泄漏:

kotlin 复制代码
// ① 集合不断增大 --- 没有"该死"的 entry
object DataCache {
    val cache = mutableMapOf<String, ByteArray>()
    fun cacheData(key: String, data: ByteArray) {
        cache[key] = data  // 不断 put,从不 remove
    }
}
kotlin 复制代码
// ② 线程池累积 --- 线程没有终点回调
object ThreadPool {
    val executor = Executors.newCachedThreadPool()
    fun runTask() {
        executor.submit {
            // 每个线程跑完就结束了,但线程池一直在
        }
    }
}
kotlin 复制代码
// ③ 资源泄漏 --- 不是对象泄漏
fun readFile() {
    val cursor = db.query(...)  // 忘了关 cursor
    // cursor 对象被 GC 回收后,底层 sqlite 连接没释放
}

能检测到泄漏,但原因链指向单一维度的:

kotlin 复制代码
// LeakCanary 能发现 Activity 泄漏
class MainActivity : Activity() {
    val handler = object : Handler() {
        override fun handleMessage(msg: Message) {
            // 匿名内部类持有外部 Activity 引用
        }
    }
    override fun onCreate(...) {
        handler.sendMessageDelayed(someMsg, 60000)
    }
    // onDestroy 没有 removeCallbacks
}
// → 检测链路:GC Root → HandlerThread → Handler → MainActivity(泄漏了)
// 原因指向 Handler,但 Message 对象本身不会被 watch

一句话总结 LeakCanary 的边界

LeakCanary 只检测"对象泄漏"------知道它该死(有 T0),发现它没死。

它不检测"资源泄漏"(忘了关)、"分配问题"(new 太多)、"增长型泄漏"(没有 T0)。

它是开发阶段最有效的第一道防线,但不是万能工具。配合 MAT、Profiler、StrictMode 才能覆盖更多的内存问题。


十三、核心设计哲学

"只有知道对象什么时候该死,才能判断它是不是死了。"

这句话直接决定了 LeakCanary 的整个架构:

  • 为什么需要 ActivityLifecycleCallbacks?------为了知道 onDestroy 的时间点(T0)
  • 为什么需要 ReferenceQueue?------为了判断对象是否被回收(T1 验证)
  • 为什么 5 秒 + 5 次 GC?------因为 GC 是不确定的,需要提高置信度
  • 为什么不能监控所有对象?------因为大部分对象没有 T0

LeakCanary 不是通用内存泄漏检测工具,它是一个围绕 onDestroy 生命周期做精细检测的工具。理解它的边界,比理解它的代码更重要。

相关推荐
沉默王二1 小时前
腾讯面试官:“你说你做了一个终端Agent,那说说 LLM 和 Agent的区别,ReAct、MCP、Tool、Memory、Skills?”我信誓旦旦开始背了
面试·agent·腾讯
爱学习的执念3 小时前
软件测试面试常问,主要考察你对接口测试相关知识的掌握程度?
面试·职场和发展
HeiSenBerg3 小时前
Glide 原理分析
面试
清泓y4 小时前
UE 物理系统知识分享
面试·ue5·游戏程序
KaifuZeng5 小时前
电源面试问题汇总一
单片机·嵌入式硬件·面试·电路
HeiSenBerg5 小时前
ARouter 原理深度剖析:从 APT 到 ASM,彻底搞懂路由框架
面试
程序员清风5 小时前
AI不是万能的,大家要专注实践!
java·后端·面试
清泓y6 小时前
UE移动开发技术面试题
android·面试·ue5·ue4·游戏程序
刘沅6 小时前
Redis的哨兵机制
后端·面试