关于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
相关产品推荐
相关产品推荐

