Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HandlerThread from android.widget.Filter seems to leak the filter which leaks the adapter #10

Closed
cypressious opened this issue May 9, 2015 · 9 comments

Comments

@cypressious
Copy link
Contributor

I got a couple of similar reports like this (real device)

* de.busliniensuche.android.MainActivity has leaked:
    * GC ROOT thread android.os.HandlerThread.<Java Local> (named 'Filter')
    * references android.os.MessageQueue.mMessages
    * references android.os.Message.target
    * references android.widget.Filter$RequestHandler.this$0
    * references de.busliniensuche.android.view.StationSearchListAdapter$StationSearchFilter.stationSearchListAdapter
    * references de.busliniensuche.android.view.StationSearchListAdapter.animListView
    * references de.busliniensuche.android.view.AnimationListView.mContext
    * leaks de.busliniensuche.android.MainActivity instance
    * Reference Key: ae18be53-5a9d-4358-a7d5-479f38fa6c90
    * Device: LGE google Nexus 4 occam
    * Android Version: 5.1.1 API: 22
    * Durations: watch=5194ms, gc=277ms, heap dump=8276ms, analysis=42454ms

or this (Genymotion)

 * de.busliniensuche.android.StationSearchFragment has leaked:
    * GC ROOT thread android.os.HandlerThread.<Java Local> (named 'Filter')
    * references android.os.MessageQueue.mMessages
    * references android.os.Message.target
    * references android.widget.Filter$RequestHandler.this$0
    * references de.busliniensuche.android.view.StationSearchListAdapter$StationSearchFilter.stationSearchListAdapter
    * references de.busliniensuche.android.view.StationSearchListAdapter.listener
    * leaks de.busliniensuche.android.StationSearchFragment instance
    * Reference Key: f4768f4c-452d-40cc-936a-efa2282dda06
    * Device: Genymotion generic Google Nexus 4 - 5.1.0 - API 22 - 768x1280 vbox86p
    * Android Version: 5.1 API: 22
    * Durations: watch=5035ms, gc=127ms, heap dump=574ms, analysis=7063ms

As far as I can tell, the root issue is the leak of StationSearchFilter which holds a reference to the adapter which in turn leaks all kinds of stuff.

Now, I can't figur out what exactly is causing the leak. It seems the HandlerThread that is started in Filter.filter isn't properly closed and therefore leaks a message.

@cypressious
Copy link
Contributor Author

Can It be a false positive? I can't even find a thread named "Filter" in the heap dump.

Edit: The thread is definitely stopped here. I think this is a false positive.

@pyricau
Copy link
Member

pyricau commented May 9, 2015

As you pointed out, the thread is definitely getting stopped. However, the FINISH_TOKEN message is always sent 3 seconds after the last FILTER_TOKEN message is received. Do you call Filter.setDelayer() ? If yes, what's your delay value?

LeakCanary has a default executor that waits 5 seconds after the main thread is idle. So this should still be good, provided that you don't have a filter delayer with really high values.

A possible explanation might be that you still call Filter.filter() after the fragment / activity has been destroyed.

@cypressious
Copy link
Contributor Author

In any case, this will never lead to a real leak, right? If true, I'd like to exclude this somehow from the leak detection. Is this possible?

@pyricau
Copy link
Member

pyricau commented May 9, 2015

In other words: when / how do you call Filter.filter() ?

You could investigate this by opening the heap dump and checking what messages you have in MessageQueue.mMessages (it's a linked list) for that specific handler thread.

@cypressious
Copy link
Contributor Author

I call it in a TextWatcher in the same fragment.

Like I said, I can't even find the thread in the heap dump. I'm guessing that in the time that the dump takes to be created the thread is already collected.

@pyricau
Copy link
Member

pyricau commented May 9, 2015

The thread exists in the heap dump, otherwise the leak trace would not show it :) . Look for instances of HandlerThread, and then look for one with the field "name" set to "Filter"

This could be a real leak. If you never stop calling Filter.filter() on that instance, then the thread will never stop, the Filter won't be GCed, and the destroyed activity won't be GCed as well.

If we find that this is a real general android leak, we can add it to the library list of excluded references.

However, if it's not and you still want to ignore it, you can but it's going to be more manual. You need to create your own version of HeapAnalyzerService, and replace createAppDefaults() with something that adds excluded refs to it. Then create your own version of ServiceHeapDumpListener that starts that service, and create the RefWatcher with it.

We might consider changing the API to allow for easier customization of the ignored refs, but in the meantime you can still roll your own :) .

@pyricau
Copy link
Member

pyricau commented May 9, 2015

It'd be nice to get to the bottom of this. It seems that Filter should have a close() method that would immediately trigger the thread killing, instead of waiting an extra 3 seconds.

@pyricau
Copy link
Member

pyricau commented May 9, 2015

Also, there is a way for you to prevent this from happening: set the StationSearchListAdapter$StationSearchFilter.stationSearchListAdapter field to null when the list view is detached.

@cypressious
Copy link
Contributor Author

For starters I created issue #12 to track the exclude API.

You're right in that the HandlerThread is present in the heap dump. However it is really gone in a dump I manually created moments later. I think this is really just a false positive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants