leakcanary icon indicating copy to clipboard operation
leakcanary copied to clipboard

TextClock memory leak

Open namgk opened this issue 3 years ago • 2 comments

https://developer.android.com/reference/android/widget/TextClock

LeakTrace information

5446 bytes retained by leaking objects
Signature: 24ae8ab7f39ddd58d1df561358903f7b13726
┬───
│ GC Root: Global variable in native code
│
├─ android.database.ContentObserver$Transport instance
│    Leaking: UNKNOWN
│    Retaining 60 B in 3 objects
│    ↓ ContentObserver$Transport.mContentObserver
│                                ~~~~~~~~~~~~~~~~
├─ android.widget.TextClock$FormatChangeObserver instance
│    Leaking: UNKNOWN
│    Retaining 32 B in 2 objects
│    ↓ TextClock$FormatChangeObserver.this$0
│                                     ~~~~~~
├─ android.widget.TextClock instance
│    Leaking: UNKNOWN
│    Retaining 1.9 kB in 23 objects
│    View not part of a window view hierarchy
│    View.mAttachInfo is null (view detached)
│    View.mID = R.id.time
│    View.mWindowAttachCount = 6
│    mContext instance of dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper, wrapping
│    activity omitted.ui.MainActivity with mDestroyed = false
│    ↓ View.mParent
│           ~~~~~~~
├─ android.widget.LinearLayout instance
│    Leaking: UNKNOWN
│    Retaining 6.6 kB in 158 objects
│    View not part of a window view hierarchy
│    View.mAttachInfo is null (view detached)
│    View.mWindowAttachCount = 6
│    mContext instance of dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper, wrapping
│    activity omitted.ui.MainActivity with mDestroyed = false
│    ↓ View.mParent
│           ~~~~~~~
╰→ android.widget.FrameLayout instance
​     Leaking: YES (ObjectWatcher was watching this because omitted.ui.ads.AdFragmentClock received
​     Fragment#onDestroyView() callback (references to its views should be cleared to prevent leaks))
​     Retaining 5.4 kB in 142 objects
​     key = f66af549-a226-4bac-af84-61059cefc47b
​     watchDurationMillis = 13975
​     retainedDurationMillis = 8969
​     View not part of a window view hierarchy
​     View.mAttachInfo is null (view detached)
​     View.mWindowAttachCount = 6
​     mContext instance of dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper, wrapping
​     activity omitted.ui.MainActivity with mDestroyed = false

Metadata

METADATA
    
    Please include this in bug reports and Stack Overflow questions.
    
    Build.VERSION.SDK_INT: 25
    Build.MANUFACTURER: rockchip
    LeakCanary version: 2.7
    App process name: omitteed
    Stats: LruCache[maxSize=3000,hits=3255,misses=61980,hitRate=4%]
    RandomAccess[bytes=2978775,reads=61980,travel=28206469105,range=13379141,size=20842769]
    Heap dump reason: 6 retained objects, app is visible
    Analysis duration: 14796 ms
    Heap dump file path: /storage/emulated/0/Download/leakcanary-omitted/2021-07-12_20-20-48_751.hprof
    Heap dump timestamp: 1626146466029
    Heap dump duration: 2089 ms
    ====================================

namgk avatar Jul 13 '21 03:07 namgk

Sounds like a bug in either the Android SDK or a manufacturer impl, you should file an issue with a repro case, which means you need to figure out how to repro first.

Sources for TextClock are here: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/widget/TextClock.java

Also @romainguy wrote TextClock, I'm sure he'll love being pinged 🤪

pyricau avatar Sep 15 '21 00:09 pyricau

The trace indicates that ContentObserver$Transport.mContentObserver isn't null, and it is set to null when ContentObserver$Transport#releaseContentObserver() is called, so that wasn't called, which means ContentResolver.unregisterContentObserver() wasn't called which means TextClock.unregisterObserver() wasn't called.

However we can see it being called in TextClock.onDetachFromWindow():

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        if (mRegistered) {
            unregisterReceiver();
            unregisterObserver();

            mRegistered = false;
        }
    }

The only way I can see this happening is if TextClock.setShowCurrentUserTime(boolean showCurrentUserTime) is called, as it's the only place adding non symmetric called, and not checking / updating the mRegistered state. Looks like it's only used in KeyguardClockSwitch and SplitClockView.

To investigate further we'd need a heap dump.

pyricau avatar Sep 15 '21 00:09 pyricau

No heap dump, no repro case and no issue filed to Google. Closing.

pyricau avatar Nov 09 '22 17:11 pyricau

I have the same issue! How can I pass a heapdump to you?

JensSchmitt avatar Jun 21 '23 06:06 JensSchmitt

You can upload directly to google issues, or you can uploading to google drive then share the link here and make sure it's shared with my email (py.ricau gmail.com)

pyricau avatar Jun 28 '23 04:06 pyricau