You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于Jetpack Compose相关LeakyCanary泄漏报告的分析与解决咨询

D/LeakCanary: │ GC Root: Thread object
D/LeakCanary: │
D/LeakCanary: ├─ android.os.HandlerThread instance
D/LeakCanary: │    Leaking: NO (PathClassLoader↓ is not leaking)
D/LeakCanary: │    Thread name: 'plumber-android-leaks'
D/LeakCanary: │    ↓ Thread.contextClassLoader
D/LeakCanary: ├─ dalvik.system.PathClassLoader instance
D/LeakCanary: │    Leaking: NO (SnapshotKt↓ is not leaking and A ClassLoader is never leaking)
D/LeakCanary: │    ↓ ClassLoader.runtimeInternalObjects
D/LeakCanary: ├─ java.lang.Object[] array
D/LeakCanary: │    Leaking: NO (SnapshotKt↓ is not leaking)
D/LeakCanary: │    ↓ Object[213]
D/LeakCanary: ├─ androidx.compose.runtime.snapshots.SnapshotKt class
D/LeakCanary: │    Leaking: NO (Recomposer↓ is not leaking and a class is never leaking)
D/LeakCanary: │    ↓ static SnapshotKt.applyObservers
D/LeakCanary: ├─ java.util.ArrayList instance
D/LeakCanary: │    Leaking: NO (Recomposer↓ is not leaking)
D/LeakCanary: │    ↓ ArrayList[2]
D/LeakCanary: ├─ androidx.compose.runtime.Recomposer$recompositionRunner$2$unregisterApplyObserver$1 instance
D/LeakCanary: │    Leaking: NO (Recomposer↓ is not leaking)
D/LeakCanary: │    Anonymous subclass of kotlin.jvm.internal.Lambda
D/LeakCanary: │    ↓ Recomposer$recompositionRunner$2$unregisterApplyObserver$1.this$0
D/LeakCanary: ├─ androidx.compose.runtime.Recomposer instance
D/LeakCanary: │    Leaking: NO (Recomposer is in state PendingWork)
D/LeakCanary: │    ↓ Recomposer.snapshotInvalidations
D/LeakCanary: │                 ~~~~~~~~~~~~~~~~~~~~~
D/LeakCanary: ├─ java.util.LinkedHashSet instance
D/LeakCanary: │    Leaking: UNKNOWN
D/LeakCanary: │    Retaining 334.1 kB in 7548 objects
D/LeakCanary: │    ↓ LinkedHashSet[element()]
D/LeakCanary: │                   ~~~~~~~~~~~
D/LeakCanary: ├─ androidx.compose.runtime.ParcelableSnapshotMutableState instance
D/LeakCanary: │    Leaking: UNKNOWN
D/LeakCanary: │    Retaining 72 B in 4 objects
D/LeakCanary: │    ↓ SnapshotMutableStateImpl.next
D/LeakCanary: │                               ~~~~
D/LeakCanary: ├─ androidx.compose.runtime.SnapshotMutableStateImpl$StateStateRecord instance
D/LeakCanary: │    Leaking: UNKNOWN
D/LeakCanary: │    Retaining 56 B in 3 objects
D/LeakCanary: │    ↓ SnapshotMutableStateImpl$StateStateRecord.value
D/LeakCanary: │                                                ~~~~~
D/LeakCanary: ├─ androidx.compose.ui.platform.AndroidComposeView$ViewTreeOwners instance
D/LeakCanary: │    Leaking: UNKNOWN
D/LeakCanary: │    Retaining 16 B in 1 objects
D/LeakCanary: │    lifecycleOwner instance of ac.mdiq.mliq.elements.PEActivity with mDestroyed = true
D/LeakCanary: │    savedStateRegistryOwner instance of ac.mdiq.mliq.elements.PEActivity with mDestroyed = true
D/LeakCanary: │    ↓ AndroidComposeView$ViewTreeOwners.lifecycleOwner
D/LeakCanary: │                                        ~~~~~~~~~~~~~~
D/LeakCanary: ╰→ ac.mdiq.mliq.elements.PEActivity instance
D/LeakCanary: ​     Leaking: YES (ObjectWatcher was watching this because ac.mdiq.mliq.elements.PEActivity received
D/LeakCanary: ​     Activity#onDestroy() callback and Activity#mDestroyed is true)
D/LeakCanary: ​     Retaining 39.3 kB in 763 objects
D/LeakCanary: ​     key = e1091306-ead4-451a-b24c-58f91dde885c
D/LeakCanary: ​     watchDurationMillis = 5519
D/LeakCanary: ​     retainedDurationMillis = 512
D/LeakCanary: ​     mApplication instance of ac.mdiq.mliq.general.Application
D/LeakCanary: ​     mBase instance of androidx.appcompat.view.ContextThemeWrapper
D/LeakCanary: ====================================
内存泄漏原因分析

从泄漏链路可明确:

  • 已销毁的PEActivity被AndroidComposeView$ViewTreeOwners的lifecycleOwner持有,该ViewTreeOwners又关联到SnapshotMutableStateImpl$StateStateRecord的value。
  • 最终这个状态记录被Recomposer的snapshotInvalidations集合留存,而Recomposer处于PendingWork状态,未在Activity销毁后及时清理无效的状态引用,导致PEActivity无法被GC回收。

核心问题是Compose状态管理与Activity生命周期解绑不及时,Recomposer仍在跟踪已失效的组件状态。

解决方案
  • 绑定状态与生命周期:在Composable中使用DisposableEffect监听Lifecycle.Event.ON_DESTROY,清理关联的状态引用或移除回调。示例:

    DisposableEffect(Unit) {
        onDispose {
            // 重置全局状态、移除回调等清理操作
        }
    }
    

    使用remember创建状态时,确保其作用域与Activity生命周期一致,避免跨生命周期持有引用。

  • 升级Compose版本:该泄漏属于Compose已知生命周期管理bug,建议升级到1.4.0及以上的稳定版本,官方已修复相关问题。

  • 清理全局/静态状态:若存在全局SnapshotMutableState或静态变量引用Activity相关对象,需在Activity.onDestroy()中手动解除引用。

  • 手动终止Recomposer:在Activity的onDestroy()方法中,获取当前Recomposer并调用cancel(),强制终止其工作并清理待处理状态。示例:

    override fun onDestroy() {
        super.onDestroy()
        val recomposer = LocalRecomposer.current
        recomposer.cancel()
    }
    

    注意:需确保此操作不会影响其他活跃的Compose组件。

  • 检查自定义State实现:若使用自定义ParcelableSnapshotMutableState,需确保其内部逻辑在生命周期结束时正确释放所有持有Activity的引用,避免隐式泄漏。

内容的提问来源于stack exchange,提问作者LXJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:08:10