Open Yash-Garg opened 2 years ago
I am facing a memory leak when using this library. I think it's probably due to the Composition used in bonsai/core/tree/Tree.kt.
Composition
There is a method for disposing the composition (composition.dispose()) which isn't getting called. It would be great if you can have a look at it.
composition.dispose()
Tried adding myComposeView.disposeComposition() in onStop() of Fragment, but got no luck.
myComposeView.disposeComposition()
onStop()
[Docs Reference](https://developer.android.com/reference/kotlin/androidx/compose/runtime/Composition#dispose())
D/LeakCanary: D/LeakCanary: ==================================== D/LeakCanary: HEAP ANALYSIS RESULT D/LeakCanary: ==================================== D/LeakCanary: 3 APPLICATION LEAKS D/LeakCanary:D/LeakCanary: References underlined with "~~~" are likely causes. D/LeakCanary: Learn more at https://squ.re/leaks. D/LeakCanary:D/LeakCanary: 2794 bytes retained by leaking objects D/LeakCanary: Signature: 294c14f86ef1f535d2d18c00e3aefe49abd3e220 D/LeakCanary: ┬─── D/LeakCanary: │ GC Root: Input or output parameters in native code D/LeakCanary: │ 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[6667] 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[0] 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 (CompositionImpl↓ is not leaking and Recomposer is in state Idle) D/LeakCanary: │ ↓ Recomposer.knownCompositions D/LeakCanary: ├─ java.util.ArrayList instance D/LeakCanary: │ Leaking: NO (CompositionImpl↓ is not leaking) D/LeakCanary: │ ↓ ArrayList[0] D/LeakCanary: ├─ androidx.compose.runtime.CompositionImpl instance D/LeakCanary: │ Leaking: NO (Composition not disposed) D/LeakCanary: │ ↓ CompositionImpl.composer D/LeakCanary: │ ~~~~~~~~ D/LeakCanary: ├─ androidx.compose.runtime.ComposerImpl instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 9.1 kB in 48 objects D/LeakCanary: │ ↓ ComposerImpl.parentProvider D/LeakCanary: │ ~~~~~~~~~~~~~~ D/LeakCanary: ├─ androidx.compose.runtime.external.kotlinx.collections.immutable.implementations.immutableMap.PersistentHashMap D/LeakCanary: │ instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 320 B in 8 objects D/LeakCanary: │ ↓ PersistentHashMap.node D/LeakCanary: │ ~~~~ D/LeakCanary: ├─ androidx.compose.runtime.external.kotlinx.collections.immutable.implementations.immutableMap.TrieNode instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 296 B in 7 objects D/LeakCanary: │ ↓ TrieNode.buffer D/LeakCanary: │ ~~~~~~ D/LeakCanary: ├─ java.lang.Object[] array D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 272 B in 6 objects D/LeakCanary: │ ↓ Object[45] D/LeakCanary: │ ~~~~ D/LeakCanary: ├─ androidx.compose.runtime.StaticValueHolder instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 12 B in 1 objects D/LeakCanary: │ ↓ StaticValueHolder.value D/LeakCanary: │ ~~~~~ D/LeakCanary: ├─ androidx.fragment.app.FragmentViewLifecycleOwner instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 173 B in 6 objects D/LeakCanary: │ ↓ FragmentViewLifecycleOwner.mFragment D/LeakCanary: │ ~~~~~~~~~ D/LeakCanary: ╰→ dev.yashgarg.qbit.ui.torrent.tabs.TorrentFilesFragment instance D/LeakCanary: Leaking: YES (ObjectWatcher was watching this because dev.yashgarg.qbit.ui.torrent.tabs.TorrentFilesFragment D/LeakCanary: received Fragment#onDestroy() callback and Fragment#mFragmentManager is null) D/LeakCanary: Retaining 2.8 kB in 105 objects D/LeakCanary: key = 729161f9-e2c4-4211-a526-d0a54992cd9e D/LeakCanary: watchDurationMillis = 5484 D/LeakCanary: retainedDurationMillis = 483 D/LeakCanary:D/LeakCanary: 210 bytes retained by leaking objects D/LeakCanary: Signature: cd1c7c9324750e1908bfb93e73a7daaccc8ddf30 D/LeakCanary: ┬─── D/LeakCanary: │ GC Root: Input or output parameters in native code D/LeakCanary: │ 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[6667] 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[0] 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 (CompositionImpl↓ is not leaking and Recomposer is in state Idle) D/LeakCanary: │ ↓ Recomposer.knownCompositions D/LeakCanary: ├─ java.util.ArrayList instance D/LeakCanary: │ Leaking: NO (CompositionImpl↓ is not leaking) D/LeakCanary: │ ↓ ArrayList[0] D/LeakCanary: ├─ androidx.compose.runtime.CompositionImpl instance D/LeakCanary: │ Leaking: NO (Composition not disposed) D/LeakCanary: │ ↓ CompositionImpl.slotTable D/LeakCanary: │ ~~~~~~~~~ D/LeakCanary: ├─ androidx.compose.runtime.SlotTable instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 345 B in 17 objects D/LeakCanary: │ ↓ SlotTable.slots D/LeakCanary: │ ~~~~~ D/LeakCanary: ├─ java.lang.Object[] array D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 6.4 kB in 206 objects D/LeakCanary: │ ↓ Object[32] D/LeakCanary: │ ~~~~ D/LeakCanary: ├─ androidx.compose.ui.platform.DisposableSaveableStateRegistry instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 37 B in 2 objects D/LeakCanary: │ ↓ DisposableSaveableStateRegistry.onDispose D/LeakCanary: │ ~~~~~~~~~ D/LeakCanary: ├─ androidx.compose.ui.platform.DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$1 instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 21 B in 1 objects D/LeakCanary: │ Anonymous subclass of kotlin.jvm.internal.Lambda D/LeakCanary: │ ↓ DisposableSaveableStateRegistry_androidKt$DisposableSaveableStateRegistry$1.$androidxRegistry D/LeakCanary: │ ~~~~~~~~~~~~~~~~~ D/LeakCanary: ├─ androidx.savedstate.SavedStateRegistry instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 506 B in 19 objects D/LeakCanary: │ ↓ SavedStateRegistry.components D/LeakCanary: │ ~~~~~~~~~~ D/LeakCanary: ├─ androidx.arch.core.internal.SafeIterableMap instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 483 B in 18 objects D/LeakCanary: │ ↓ SafeIterableMap["androidx.lifecycle.internal.SavedStateHandlesProvider"] D/LeakCanary: │ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ D/LeakCanary: ├─ androidx.lifecycle.SavedStateHandlesProvider instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 251 B in 10 objects D/LeakCanary: │ ↓ SavedStateHandlesProvider.viewModel$delegate D/LeakCanary: │ ~~~~~~~~~~~~~~~~~~ D/LeakCanary: ├─ kotlin.SynchronizedLazyImpl instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 230 B in 9 objects D/LeakCanary: │ ↓ SynchronizedLazyImpl._value D/LeakCanary: │ ~~~~~~ D/LeakCanary: ╰→ androidx.lifecycle.SavedStateHandlesVM instance D/LeakCanary: Leaking: YES (ObjectWatcher was watching this because androidx.lifecycle.SavedStateHandlesVM received D/LeakCanary: ViewModel#onCleared() callback) D/LeakCanary: Retaining 210 B in 8 objects D/LeakCanary: key = 375b335d-00db-4565-8c6d-0848ea7ca599 D/LeakCanary: watchDurationMillis = 5485 D/LeakCanary: retainedDurationMillis = 483 D/LeakCanary:D/LeakCanary: 15324 bytes retained by leaking objects D/LeakCanary: Signature: 3074046c89ba82b393e6e7ba1f02c8a330683dbd D/LeakCanary: ┬─── D/LeakCanary: │ GC Root: Input or output parameters in native code D/LeakCanary: │ 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[6667] 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[0] 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 (CompositionImpl↓ is not leaking and Recomposer is in state Idle) D/LeakCanary: │ ↓ Recomposer.knownCompositions D/LeakCanary: ├─ java.util.ArrayList instance D/LeakCanary: │ Leaking: NO (CompositionImpl↓ is not leaking) D/LeakCanary: │ ↓ ArrayList[0] D/LeakCanary: ├─ androidx.compose.runtime.CompositionImpl instance D/LeakCanary: │ Leaking: NO (Composition not disposed) D/LeakCanary: │ ↓ CompositionImpl.parent D/LeakCanary: │ ~~~~~~ D/LeakCanary: ├─ androidx.compose.runtime.ComposerImpl$CompositionContextImpl instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 190 B in 6 objects D/LeakCanary: │ ↓ ComposerImpl$CompositionContextImpl.this$0 D/LeakCanary: │ ~~~~~~ D/LeakCanary: ├─ androidx.compose.runtime.ComposerImpl instance D/LeakCanary: │ Leaking: UNKNOWN D/LeakCanary: │ Retaining 17.2 kB in 54 objects D/LeakCanary: │ ↓ ComposerImpl.composition D/LeakCanary: │ ~~~~~~~~~~~ D/LeakCanary: ├─ androidx.compose.runtime.CompositionImpl instance D/LeakCanary: │ Leaking: YES (Composition disposed) D/LeakCanary: │ Retaining 15.3 kB in 388 objects D/LeakCanary: │ ↓ CompositionImpl.observations D/LeakCanary: ├─ androidx.compose.runtime.collection.IdentityScopeMap instance D/LeakCanary: │ Leaking: YES (CompositionImpl↑ is leaking) D/LeakCanary: │ Retaining 13.2 kB in 356 objects D/LeakCanary: │ ↓ IdentityScopeMap.scopeSets D/LeakCanary: ├─ androidx.compose.runtime.collection.IdentityArraySet[] array D/LeakCanary: │ Leaking: YES (CompositionImpl↑ is leaking) D/LeakCanary: │ Retaining 10.5 kB in 252 objects D/LeakCanary: │ ↓ IdentityArraySet[4] D/LeakCanary: ├─ androidx.compose.runtime.collection.IdentityArraySet instance D/LeakCanary: │ Leaking: YES (CompositionImpl↑ is leaking) D/LeakCanary: │ Retaining 148 B in 5 objects D/LeakCanary: │ ↓ IdentityArraySet.values D/LeakCanary: ├─ java.lang.Object[] array D/LeakCanary: │ Leaking: YES (CompositionImpl↑ is leaking) D/LeakCanary: │ Retaining 132 B in 4 objects D/LeakCanary: │ ↓ Object[0] D/LeakCanary: ├─ androidx.compose.runtime.RecomposeScopeImpl instance D/LeakCanary: │ Leaking: YES (CompositionImpl↑ is leaking) D/LeakCanary: │ Retaining 68 B in 3 objects D/LeakCanary: │ ↓ RecomposeScopeImpl.block D/LeakCanary: ├─ androidx.compose.ui.platform.ComposeView$Content$1 instance D/LeakCanary: │ Leaking: YES (CompositionImpl↑ is leaking) D/LeakCanary: │ Retaining 20 B in 1 objects D/LeakCanary: │ Anonymous subclass of kotlin.jvm.internal.Lambda D/LeakCanary: │ ↓ ComposeView$Content$1.$tmp1_rcvr D/LeakCanary: ╰→ androidx.compose.ui.platform.ComposeView instance D/LeakCanary: Leaking: YES (ObjectWatcher was watching this because dev.yashgarg.qbit.ui.torrent.tabs.TorrentFilesFragment D/LeakCanary: received Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks)) D/LeakCanary: Retaining 2.4 kB in 39 objects D/LeakCanary: key = d15cfab8-cfee-4de6-82f2-9e5e4d4f6d02 D/LeakCanary: watchDurationMillis = 5489 D/LeakCanary: retainedDurationMillis = 488 D/LeakCanary: View not part of a window view hierarchy D/LeakCanary: View.mAttachInfo is null (view detached) D/LeakCanary: View.mID = R.id.filesComposeView D/LeakCanary: View.mWindowAttachCount = 1 D/LeakCanary: mContext instance of dev.yashgarg.qbit.MainActivity with mDestroyed = false D/LeakCanary: ====================================
Hi I'm encountering memory leak of this library as well, do you need help fixing this issue?
I am facing a memory leak when using this library. I think it's probably due to the
Composition
used in bonsai/core/tree/Tree.kt.There is a method for disposing the composition (
composition.dispose()
) which isn't getting called. It would be great if you can have a look at it.Tried adding
myComposeView.disposeComposition()
inonStop()
of Fragment, but got no luck.[Docs Reference](https://developer.android.com/reference/kotlin/androidx/compose/runtime/Composition#dispose())
Leakcanary Stacktrace