12

いつものように、長い問題の説明です。

現在、製品のストレス テストを行っていますが、奇妙な問題に直面しています。1 ~ 2 時間後、ヒープ領域が拡大し始め、しばらくしてアプリケーションが停止します。

アプリケーションをプロファイリングすると、非常に大量の Finalizer オブジェクトが表示され、ヒープがいっぱいになります。さて、私たちは「奇妙なファイナライザー スレッドが遅くなる可能性がある」と考え、ファイナライズする必要があるオブジェクト (この場合は JNA ネイティブ ハンドル) の量を減らす方法を検討しました。とにかく良いアイデアで、何千もの新しいオブジェクトを減らしました...

次のテストでは同じパターンが示されましたが、わずか 1 時間後にはそれほど急勾配ではありませんでした。今回のファイナライザーは、テストベッドで頻繁に使用される FileInput ストリームと FileOutput ストリームに由来します。すべてのリソースは閉じられていますが、ファイナライザーはもうクリーンアップされていません。

1 時間または 2 時間後 (例外なし)、FinalizerThread が突然動作を停止したように見える理由がわかりません。一部のスレッドで手動で System.runFinalization() を強制すると、プロファイラーはファイナライザーがクリーンアップされたことを示します。テストをすぐに再開すると、ファイナライザーに新しいヒープが割り当てられます。

FinalizerThread はまだそこにあり、jConsole に待機中であることを尋ねています。

編集

まず、HeapAnalyzer を使用してヒープを調べたところ、新しいことや奇妙なことは何も明らかになりませんでした。HeapAnalyzer にはいくつかの優れた機能がありますが、最初は苦労しました。私は jProfiler を使用しています。これには優れたヒープ検査ツールが付属しており、そのまま使用できます。

たぶん、HeapAnalyzer のいくつかのキラー機能が欠けているのでしょうか?

次に、今日、プロファイラーの代わりにデバッグ接続を使用してテストをセットアップしました。システムは現在、5 時間近く安定しています。これは、あまりにも多くのファイナライザー (最初のレビューで削減されたもの)、プロファイラー、および VM GC 戦略の非常に奇妙な組み合わせのようです。現時点ではすべてが正常に動作しているため、実際の洞察はありません...

これまでの情報に感謝します - 引き続き注目して興味を持っていただければ幸いです (これで、単純なプログラミングの誤りについては話さないと信じる理由が増えたかもしれません)。

4

6 に答える 6

3

現状をまとめて、この質問を締めくくりたいと思います。

最後のテストは、問題なく 60 時間以上経過しました。これにより、次の要約/結論が導き出されます。

  • 最終的に「ファイナライズ」を実装する多くのオブジェクトを使用する高スループットサーバーがあります。これらのオブジェクトは、ほとんどが JNA メモリ ハンドルとファイル ストリームです。GC およびファイナライザー スレッドがクリーンアップできるよりも速くファイナライザーをビルドすると、このプロセスは約 3 時間後に失敗します。これはよく知られた現象です (-> google)。
  • サーバーがほぼすべての JNA ファイナライザーを取り除くように、いくつかの最適化を行いました。このバージョンは、jProfiler を添付してテストされました。
  • サーバーは、最初の試行より数時間後に停止しました...
  • プロファイラーは大量のファイナライザーを示しましたが、今回は主にファイル ストリームのみが原因でした。サーバーをしばらく一時停止した後でも、このキューはクリーンアップされませんでした。
  • 「System.runFinalization()」を手動でトリガーした後でのみ、キューが空になりました。サーバーを再開すると補充が開始されました...
  • これはいまだに不可解です。これは、GC/ファイナライズとの何らかのプロファイラーの相互作用であると推測されます。
  • 非アクティブなファイナライザー スレッドの原因をデバッグするために、今回はプロファイラーを切り離し、デバッガーをアタッチしました。
  • システムは目立った欠陥なしに実行されていました... FinalizerThread と GC はすべて「緑色」です。
  • テストを再開し (今回は jConsole 以外にエージェントを接続せずに)、60 時間以上正常に動作しています。したがって、最初の JNA リファクタリングで問題が解決したようです。

ファイナライザーを管理するためのその他の戦略については、たとえばhttp://cleversoft.wordpress.com/2011/05/14/out-of-memory-exception-from-finalizer-object-overflow/で説明されています(あまり賢くない "ファイナライザーを使用しないでください」..)。

ご意見をお寄せいただきありがとうございます。

于 2012-05-07T10:57:09.903 に答える
1

あなたのジレンマに具体的な答えを出すのは難しいですが、ヒープ ダンプを取り、IBM の HeapAnalyzer で実行します。http://www.ibm.com/developerworksで「ヒープ アナライザー」を検索してください(直接リンクは変更され続けています)。ファイナライズをオーバーライドしていない場合、ファイナライザー スレッドが「突然動作を停止する」可能性はほとんどないようです。

于 2012-05-03T17:23:36.007 に答える
1

ファイナライザーがブロックされる可能性はありますが、単純に停止する方法はわかりません。

多くの FileInputStream および FileOutputStream finalize() メソッドがある場合、これはファイルを正しく閉じていないことを示しています。これらのストリームが finally ブロックで常に閉じられていることを確認するか、Java 7 の ARM を使用してください。(自動リソース管理)

jConsole 彼は待っています。

WAITING になるには、オブジェクトを待機している必要があります。

于 2012-05-03T17:25:21.050 に答える
1

FileInputStream と FileOutputStream の両方の finalize() メソッドに同じコメントがあります。

. . .
/*
 * Finalizer should not release the FileDescriptor if another
 * stream is still using it. If the user directly invokes
 * close() then the FileDescriptor is also released.
 */
     runningFinalize.set(Boolean.TRUE); 
. . . 

これは、ファイナライザーがストリームが解放されるのを待っている可能性があることを意味します。つまり、Joop Eggen が上で述べたように、ストリームの 1 つを閉じるときにアプリが何か悪いことをしている可能性があります。

于 2012-05-03T18:24:21.770 に答える
0

私の推測: それは、独自のストリーム (ラッパー) クラスでオーバーライドされたクローズです。new A(new B(new C()))多くの場合、ストリーム クラスはラッパーであり、他のクラスに委譲するため、そのような入れ子になっていると、クローズ時に間違ったロジックが発生する可能性があると想像できます。2 回のクロージング、デリゲートのクロージングを探す必要があります。そして、まだクローズを忘れている可能性があります (間違ったオブジェクトをクローズしますか?)。

于 2012-05-03T17:32:28.613 に答える
0

ヒープの成長が遅いため、Java ガベージ コレクターは、メモリ不足の状況で遅ればせながらガベージ コレクションを実行しようとすると、メモリ不足になる可能性があります。-XX:+UseConcMarkSweepGCを使用してコンカレント マーク アンド スイープ ガベージ コレクションをオンにして、問題が解決するかどうかを確認してください。

于 2012-05-03T17:35:56.130 に答える