我在 Android 应用程序中遇到了一个非常奇怪的问题。在某个点之后(大约在主要活动开始并显示片段时),FinalizerDaemon 停止处理对象并且垃圾不断堆积。查看线程转储,它似乎卡在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)
然而队列不是空的。如果我在使用应用程序一段时间后转储堆,则队列实际上有数千个条目。数据结构看起来也没有损坏:
在分配和垃圾收集更多之后再次转储表明队列的头部与之前的 Matrix 实例相同。
现在,我注意到了这一点,因为我保留了一些需要在某个时候释放的 C++ 对象。虽然我怀疑终结器调用 JNI 函数并在 C++ 端做一些愚蠢的事情可能会以某种方式破坏它,但我所有的日志都表明所有终结器都运行良好并且没有抛出任何东西就返回,直到它们随机停止被调用。此外,finalize 调用实际上不可能破坏守护程序,除非对整个应用程序进行段错误或其他内容,因为 Watchdog 应该处理运行时间过长并引发异常的终结器。
我尝试了一个显式的System.runFinalization()
方法,它所做的只是永远挂起主线程,等待永远不会运行的守护进程。
知道这怎么会发生吗?