web-dev-qa-db-ja.com

Android FinalizerDaemonがハングアップ

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)
_

しかし、キューは空ではありません。アプリをしばらく使用した後でヒープをダンプすると、キューは文字通り何千ものエントリの長さになります。データ構造も壊れているようには見えません: FinalizerDaemon instance showing a non-empty ReferenceQueue

割り当ててガベージコレクションを行った後で再度ダンプすると、キューの先頭が以前と同じMatrixインスタンスであることがわかります。

ある時点でリリースする必要があるいくつかのC++オブジェクトを保持しているため、これに気づきました。ファイナライザーがJNI関数を呼び出して、C++側で愚かなことをすると、何らかの理由でそれが壊れる可能性があると思いますが、すべてのログは、すべてのファイナライザーが正常に実行され、ランダムに呼び出されなくなるまで何もスローせずに戻っていることを示しています。また、Watchdogは長時間実行されて例外をスローするファイナライザーを処理することになっているため、アプリ全体などをセグフォールトする以外に、ファイナライズ呼び出しでデーモンを壊すことは実際には不可能です。

明示的なSystem.runFinalization()を試しましたが、実行されないデーモンを待って、メインスレッドを永久にハングさせるだけです。

これがどのように起こり得るかについて何か考えはありますか?

19
IvoDankolov

これは、finalizeメソッドで 復活 であるいくつかのオブジェクトに関係していると思います。

これから段落を引用します 質問

ファイナライザスレッドが実行されるため、ガベージコレクションが動作してオブジェクトに関連付けられたリソースがクリーンアップされます。私がそれを正確に見ている場合、ファイナライザーはこのオブジェクトへのロックを取得できません:Java.lang.ref.ReferenceQueue.remove(ReferenceQueue.Java:118)なぜならJavaオブジェクトはメソッドを実行しているため、ファイナライザスレッドは、そのオブジェクトが現在のタスクで終了するまでロックされます。

多分それはあなたが持っている状況です。

2
Mina Samy