I'm having a really weird problem in an Android app. After a certain point (around when the main activity starts and a fragment is displayed) the FinalizerDaemon just stops processing objects and garbage keeps piling up. Looking at a thread dump, it seems to be stuck on ReferenceQueue.remove()
:
"FinalizerDaemon@4461" daemon prio=5 waiting
java.lang.Thread.State: WAITING
at java.lang.Object.wait(Object.java:-1)
at java.lang.Object.wait(Object.java:423)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:101)
- locked <0x1173> (a java.lang.ref.ReferenceQueue)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:72)
at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:185)
at java.lang.Thread.run(Thread.java:818)
Yet the queue is not empty. If I dump the heap after using app for a while, the queue is literally thousands of entries long. The data structure also doesn't look broken:
Dumping again after allocating and garbage collecting some more shows that the head of the queue is the same Matrix instance as before.
Now, I noticed this because I'm retaining some C++ objects, which need to be released at some point. While I suspect that a finalizer calling into JNI functions and doing something stupid on the C++ side could somehow break it, all my logs indicate that all the finalizers are running fine and returning without throwing anything until they just randomly stop getting called. Also, it shouldn't really be possible for a finalize call to break the Daemon, short of segfaulting the whole app or something, since the Watchdog is supposed to handle finalizers that run for too long and throw an exception.
I tried an explicit System.runFinalization()
and all it does is hang the main thread forever, waiting for the daemon which never runs.
Any idea how this could happen?
head != null
on that queue, then it would appear that whatever the GC is doing to add references is failing to signal the queue. What version of Android? – Bannernotify()
no longer works? You could try attaching a debugger and manually advancing or clearinghead
to see if it starts working when you drop the current entry (and then create some more garbage to cause the queue to signal). – Banner