This leak looks similar to a leak which already gets distinguished as a Library leak:
https://github.com/square/leakcanary/blob/58ceeeb2da7c7a2bbeb39c41fd0d8b2c67b5e17b/shark-android/src/main/java/shark/AndroidReferenceMatchers.kt#L675
I'm not able to consistently reproduce this leak.
β¬βββ
β GC Root: System class
β
ββ android.view.WindowManagerGlobal class
β Leaking: NO (a class is never leaking)
β β static WindowManagerGlobal.sDefaultWindowManager
β ~~~~~~~~~~~~~~~~~~~~~
ββ android.view.WindowManagerGlobal instance
β Leaking: UNKNOWN
β Retaining 643 bytes in 13 objects
β β WindowManagerGlobal.mViews
β ~~~~~~
ββ java.util.ArrayList instance
β Leaking: UNKNOWN
β Retaining 60 bytes in 2 objects
β β ArrayList.elementData
β ~~~~~~~~~~~
ββ java.lang.Object[] array
β Leaking: UNKNOWN
β Retaining 40 bytes in 1 objects
β β Object[].[0]
β ~~~
ββ android.widget.LinearLayout instance
β Leaking: YES (View.mContext references a destroyed activity)
β Retaining 5952 bytes in 31 objects
β View is part of a window view hierarchy
β View.mAttachInfo is not null (view attached)
β View.mWindowAttachCount = 1
β mContext instance of eu.unmatched.lynx.ui.activities.main.MainActivity with mDestroyed = true
β β LinearLayout.mContext
β°β eu.unmatched.lynx.ui.activities.main.MainActivity instance
β Leaking: YES (ObjectWatcher was watching this because eu.unmatched.lynx.ui.activities.main.MainActivity received
β Activity#onDestroy() callback and Activity#mDestroyed is true)
β Retaining 95623 bytes in 1464 objects
β key = 8da7d617-f46c-4f03-ba19-d9fe2bbcdb49
β watchDurationMillis = 5189
β retainedDurationMillis = 187
β mApplication instance of eu.unmatched.lynx.MyApplication
β mBase instance of androidx.appcompat.view.ContextThemeWrapper, not wrapping known Android context
METADATA
Build.VERSION.SDK_INT: 27
Build.MANUFACTURER: POINTMOBILE
LeakCanary version: 2.5
App process name: eu.unmatched.lynx.lite
Stats: LruCache[maxSize=3000,hits=1636,misses=38365,hitRate=4%]
RandomAccess[bytes=1931040,reads=38365,travel=13137515160,range=12301429,size=15381381]
Analysis duration: 11947 ms
π Looks like we need to add "android.view.WindowManagerGlobal", "mViews" to WINDOW_MANAGER_GLOBAL
I took a deeper look into the heap dump (thanks for providing it!), as it seems surprising to see an attached view for a destroyed activity.
It turns out, the root view of the activity is actually detached. However, there are two extra root views that use the activity as their context but are still attached. Looking at the window attributes (title), both say "Toast".
Both toasts say "Event has been sent"
So I'm not sure this is a proper AOSP leak. Have you tried using the application context to create these toasts?
Thanks for taking a deeper look into the heap dump. You might have just given me a way to reproduce this issue!
I didn't know a Toast would still be around if it had been shown using the Activity Context without being kept in a field.
I'll try to reproduce this and I'll get back to you!
@pyricau
I was very easily able to reproduce the issue using the following project https://github.com/mickverm/Leaks on the emulator:
Produces a LeakTrace which seems to be WINDOW_MANAGER_GLOBAL in AndroidReferenceMatchers library leaks
====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS
References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.
53381 bytes retained by leaking objects
Signature: 32422f35e5d511ca7a995eb137466491deb1b0
β¬βββ
β GC Root: System class
β
ββ android.view.WindowManagerGlobal class
β Leaking: NO (ViewRootImplβ is not leaking and a class is never leaking)
β β static WindowManagerGlobal.sDefaultWindowManager
ββ android.view.WindowManagerGlobal instance
β Leaking: NO (ViewRootImplβ is not leaking)
β β WindowManagerGlobal.mRoots
ββ java.util.ArrayList instance
β Leaking: NO (ViewRootImplβ is not leaking)
β β ArrayList.elementData
ββ java.lang.Object[] array
β Leaking: NO (ViewRootImplβ is not leaking)
β β Object[].[1]
ββ android.view.ViewRootImpl instance
β Leaking: NO (ViewRootImpl#mView is not null)
β mContext instance of be.mickverm.leaks.ui.activities.MainActivity with mDestroyed = true
β β ViewRootImpl.mContext
β ~~~~~~~~
β°β be.mickverm.leaks.ui.activities.MainActivity instance
β Leaking: YES (ObjectWatcher was watching this because be.mickverm.leaks.ui.activities.MainActivity received
β Activity#onDestroy() callback and Activity#mDestroyed is true)
β Retaining 53381 bytes in 809 objects
β key = 00f5f718-350d-4ecc-ba1d-ef2d8d404dc8
β watchDurationMillis = 5117
β retainedDurationMillis = 116
β mApplication instance of android.app.Application
β mBase instance of android.app.ContextImpl, not wrapping known Android context
====================================
0 LIBRARY LEAKS
A Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over.
See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks
====================================
METADATA
Please include this in bug reports and Stack Overflow questions.
Build.VERSION.SDK_INT: 26
Build.MANUFACTURER: Google
LeakCanary version: 2.5
App process name: be.mickverm.leaks
Stats: LruCache[maxSize=3000,hits=1864,misses=28143,hitRate=6%]
RandomAccess[bytes=1572782,reads=28143,travel=5563280504,range=7844522,size=9953547]
Analysis duration: 1643 ms
Heap dump file path: /data/user/0/be.mickverm.leaks/files/leakcanary/2020-12-03_12-50-02_580.hprof
Heap dump timestamp: 1606996204693
Heap dump duration: 457 ms
====================================
Produces the LeakTrace attached in https://github.com/square/leakcanary/issues/1982#issue-731336879
Was unable to cause a leak.
Produced leak cfr. SDK 23-26 and a known library leak.
====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS
References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.
45547 bytes retained by leaking objects
Signature: 32422f35e5d511ca7a995eb137466491deb1b0
β¬βββ
β GC Root: System class
β
ββ android.view.WindowManagerGlobal class
β Leaking: NO (ViewRootImplβ is not leaking and a class is never leaking)
β β static WindowManagerGlobal.sDefaultWindowManager
ββ android.view.WindowManagerGlobal instance
β Leaking: NO (ViewRootImplβ is not leaking)
β β WindowManagerGlobal.mRoots
ββ java.util.ArrayList instance
β Leaking: NO (ViewRootImplβ is not leaking)
β β ArrayList.elementData
ββ java.lang.Object[] array
β Leaking: NO (ViewRootImplβ is not leaking)
β β Object[].[0]
ββ android.view.ViewRootImpl instance
β Leaking: NO (ViewRootImpl#mView is not null)
β mContext instance of be.mickverm.leaks.ui.activities.MainActivity with mDestroyed = true
β β ViewRootImpl.mContext
β ~~~~~~~~
β°β be.mickverm.leaks.ui.activities.MainActivity instance
β Leaking: YES (ObjectWatcher was watching this because be.mickverm.leaks.ui.activities.MainActivity received
β Activity#onDestroy() callback and Activity#mDestroyed is true)
β Retaining 45547 bytes in 822 objects
β key = 18a52c98-0f5d-437f-a966-1c7053315628
β watchDurationMillis = 29933
β retainedDurationMillis = 24931
β mApplication instance of android.app.Application
β mBase instance of android.app.ContextImpl, not wrapping known Android context
====================================
1 LIBRARY LEAKS
A Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over.
See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks
Leak pattern: native global variable referencing android.widget.Toast$TN
Description: Toast.TN is held by a global variable in native code due to an IPC call to show the toast.
90946 bytes retained by leaking objects
Displaying only 1 leak trace out of 2 with the same signature
Signature: 57b73389f44769aaf18fc472c6d6be789fe3191
β¬βββ
β GC Root: Global variable in native code
β
ββ android.widget.Toast$TN instance
β Leaking: UNKNOWN
β Retaining 3270 bytes in 26 objects
β β Toast$TN.mNextView
β ~~~~~~~~~
ββ android.widget.LinearLayout instance
β Leaking: YES (View.mContext references a destroyed activity)
β Retaining 2459 bytes in 22 objects
β View not part of a window view hierarchy
β View.mAttachInfo is null (view detached)
β View.mWindowAttachCount = 0
β mContext instance of be.mickverm.leaks.ui.activities.MainActivity with mDestroyed = true
β β LinearLayout.mContext
β°β be.mickverm.leaks.ui.activities.MainActivity instance
β Leaking: YES (ObjectWatcher was watching this because be.mickverm.leaks.ui.activities.MainActivity received
β Activity#onDestroy() callback and Activity#mDestroyed is true)
β Retaining 45473 bytes in 824 objects
β key = e0d19f5a-df5f-4e5b-9b2e-ad6ea023ba3c
β watchDurationMillis = 14176
β retainedDurationMillis = 9172
β mApplication instance of android.app.Application
β mBase instance of android.app.ContextImpl, not wrapping known Android context
====================================
METADATA
Please include this in bug reports and Stack Overflow questions.
Build.VERSION.SDK_INT: 29
Build.MANUFACTURER: Google
LeakCanary version: 2.5
App process name: be.mickverm.leaks
Stats: LruCache[maxSize=3000,hits=3431,misses=41917,hitRate=7%]
RandomAccess[bytes=2140317,reads=41917,travel=10576390520,range=12326178,size=16436075]
Analysis duration: 8922 ms
Heap dump file path: /data/user/0/be.mickverm.leaks/files/leakcanary/2020-12-03_13-16-55_691.hprof
Heap dump timestamp: 1606997826896
Heap dump duration: 2
And yes, using requireContext().applicationContext resolves this leak.
Interesting! Thx for the repro. So enqueuing toasts with activity context can create this leak. And because toasts stay in the queue until rendered, this could take a while.
Ok, I've looked into this some more. Thanks for the repro case again, that really helped.
It's our job as developers to make sure we either use an application context, or cancel toasts when the activity is destroyed (see https://twitter.com/Piwai/status/1334551845000253442). If we use the activity context, then we need to cancel the toast on destroy, and if we don't we get this leak.
The WINDOW_MANAGER_GLOBAL tagging of android.view.WindowManagerGlobal#mRoots as a LibraryLeak was a mistake of mine: this isn't really a library leak, i.e. a bug in AOSP. I added this tagging only to API 27. LeakCanary tries to find a path that doesn't go through any such tag, which is why on API 27 is went through the longer path via android.view.WindowManagerGlobal#mViews.
I'll remove this tag, and add more details to help surface these leaks.