I am experiencing a strange memory leak with Compo...
# compose
v
I am experiencing a strange memory leak with Compose with AnimatedNavHost from Accompanist and AndroidView. 1. Start from page A that contains an AndroidView 2. Navigate to page B (without popping page A) 3. (Page A is removed from the composition (onDispose called etc)) 4. Page B is popped off the stack, returning to A 5. Repeat indefinitely. Every time page A is displayed, a new View is created and the previous Views never get garbage collected. Any idea why this might be happening? Is it possible that a reference to the View could be accidentally stored somewhere or could this be a bug in Compose/Accompanist/Navigation? An additional possibly important detail: GC happens normally if A is popped off the navigation stack. EDIT: I managed to debug this and find the root cause. I will write an explanation in 🧵 later if anyone else experiences the same issue. Done.
👀 2
On the page A that was leaking memory, we had nested content like this: • Compose(1) -> AndroidView -> RecyclerView -> Compose(2) It turns out Compose doesn't release references when detaching from window if the content is inside a RecyclerView, probably because it will likely be reused soon. So compose indirectly kept a reference to the RecyclerView and all the content inside, which was causing the memory leak when new views were however being created on every navigation. We fixed this by manually calling
.callPoolingContainerOnRelease()
from https://developer.android.com/jetpack/androidx/releases/customview#customview-poolingcontainer-1.0.0 on the
onDetachedFromWindow()
of the RecyclerView. This is actually mentioned in the documentation as well: https://developer.android.com/jetpack/compose/migrate/interoperability-apis/compose-in-views#composition-strategy
[...] In some of these situations, the app can also slowly leak memory from Composition instances unless you manually call
AbstractComposeView.disposeComposition
.
What however still puzzles me is why the GC succeeded when the page was popped off the navigation stack (at least sometimes) 🤔
z
Cc @Ryan Mentley
r
Hmmm, that's really strange. That shouldn't happen; RecyclerView does call poolingcontainer's onrelease in
onDetachedFromWindow
, though it's not completely unconditional due to needing to handle, for instance, multiple RecyclerViews sharing a RecycledViewPool
I wonder if there's some edge case I missed while writing that logic.
v
Hi @Ryan Mentley, thanks for commenting on this, albeit a bit later! I reviewed the RecyclerView implementation again a while back and came to the same conclusion that it should work, but it definitely somehow didn't back in March. I tried removing our workaround on a newer version, and I couldn't reproduce the leak anymore. My assumption is that there's been some other underlying bug that prevented release being called, but it has meanwhile been fixed by some other change.
👍 1
352 Views