epoxy
epoxy copied to clipboard
Memory leak in ActivityRecyclerPool in Android API level >= 29
trafficstars
I was investigating following leak reported by leak canary:
D/LeakCanary:
┬───
│ GC Root: Input or output parameters in native code
│
├─ okio.AsyncTimeout class
│ Leaking: NO (PathClassLoader↓ is not leaking and a class is never leaking)
│ ↓ static AsyncTimeout.$class$classLoader
├─ dalvik.system.PathClassLoader instance
│ Leaking: NO (EpoxyRecyclerView↓ is not leaking and A ClassLoader is never leaking)
│ ↓ ClassLoader.runtimeInternalObjects
├─ java.lang.Object[] array
│ Leaking: NO (EpoxyRecyclerView↓ is not leaking)
│ ↓ Object[].[1720]
├─ com.airbnb.epoxy.EpoxyRecyclerView class
│ Leaking: NO (a class is never leaking)
│ ↓ static EpoxyRecyclerView.ACTIVITY_RECYCLER_POOL
│ ~~~~~~~~~~~~~~~~~~~~~~
├─ com.airbnb.epoxy.ActivityRecyclerPool instance
│ Leaking: UNKNOWN
│ Retaining 1,8 MB in 9130 objects
│ ↓ ActivityRecyclerPool.pools
│ ~~~~~
├─ java.util.ArrayList instance
│ Leaking: UNKNOWN
│ Retaining 1,8 MB in 9129 objects
│ ↓ ArrayList.elementData
│ ~~~~~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ Retaining 1,8 MB in 9128 objects
│ ↓ Object[].[0]
│ ~~~
├─ com.airbnb.epoxy.PoolReference instance
│ Leaking: UNKNOWN
│ Retaining 1,8 MB in 9127 objects
│ ↓ PoolReference.viewPool
│ ~~~~~~~~
├─ com.airbnb.epoxy.UnboundedViewPool instance
│ Leaking: UNKNOWN
│ Retaining 1,8 MB in 9125 objects
│ ↓ UnboundedViewPool.scrapHeaps
│ ~~~~~~~~~~
├─ android.util.SparseArray instance
│ Leaking: UNKNOWN
│ Retaining 1,8 MB in 9115 objects
│ ↓ SparseArray.mValues
│ ~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ Retaining 1,8 MB in 9113 objects
│ ↓ Object[].[0]
│ ~~~
├─ java.util.LinkedList instance
│ Leaking: UNKNOWN
│ Retaining 242,9 kB in 3814 objects
│ ↓ LinkedList.first
│ ~~~~~
├─ java.util.LinkedList$Node instance
│ Leaking: UNKNOWN
│ Retaining 68,5 kB in 1063 objects
│ ↓ LinkedList$Node.item
│ ~~~~
├─ com.airbnb.epoxy.EpoxyViewHolder instance
│ Leaking: UNKNOWN
│ Retaining 34,3 kB in 531 objects
│ ↓ RecyclerView$ViewHolder.itemView
│ ~~~~~~~~
├─ com.nedap.healthcare.dossier.clients.views.ClientSearchItemView instance
│ Leaking: YES (View.mContext references a destroyed activity)
│ Retaining 34,2 kB in 530 objects
│ View not part of a window view hierarchy
│ View.mAttachInfo is null (view detached)
│ View.mWindowAttachCount = 3
│ mContext instance of com.nedap.healthcare.dossier.activities.ClientSelectionActivity with mDestroyed = true
│ ↓ View.mContext
╰→ com.nedap.healthcare.dossier.activities.ClientSelectionActivity instance
Leaking: YES (ObjectWatcher was watching this because com.nedap.healthcare.dossier.activities.
ClientSelectionActivity received Activity#onDestroy() callback and Activity#mDestroyed is true)
Retaining 1,5 MB in 4113 objects
key = 8dbac52c-b7eb-4261-88ea-f8cbd8a67f57
watchDurationMillis = 5159
retainedDurationMillis = 156
mApplication instance of com.nedap.healthcare.dossier.DossierApplication
mBase instance of androidx.appcompat.view.ContextThemeWrapper
After debugging I found possible root cause.
In this method https://github.com/airbnb/epoxy/blob/master/epoxy-adapter/src/main/java/com/airbnb/epoxy/ActivityRecyclerPool.kt#L100 both isFinishing and isDestroyed flags are set to false.
After investigating more it looks like OnDestroy on API level 29 is dispatched from onActivityPreDestroyed() callback, while flag is set right after this call https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/Activity.java#8242
I've got the same issue as @ggajews :(
I've got the same issue