4

これらのことは、徹底的に分析して適切な提案を行うために、コードの綿密な検査と可用性を明らかに必要とします。それでも、それが常に可能であるとは限らず、以下に提供する情報に基づいて、良いヒントを提供できることを願っています。

リスナースレッドを使用して着信データをリッスンするサーバーアプリケーションがあります。着信データはアプリケーション固有のメッセージに解釈され、これらのメッセージによってイベントが発生します。

その時点まで、私は物事がどのように行われるかを実際に制御することはできません。

これはレガシーアプリケーションであるため、これらのイベントは以前は同じリスナースレッド(主にシングルスレッドアプリケーション)によって処理されていました。イベントはブラックボックスに送信され、ディスクに書き込む必要のある結果が出力されます。

スループットを向上させるために、イベントを処理するためにスレッドプールを採用したいと思いました。リスナースレッドは、イベントが作成されるたびに新しいタスクを生成でき、スレッドがブラックボックスの呼び出しを処理するという考え方です。最後に、ディスクへの書き込みを実行するバックグラウンドスレッドがあります。

以前のセットアップとバックグラウンドライターだけで、すべてが正常に機能し、スループットは以前の約1.6倍になります。

ただし、スレッドプールを追加すると、パフォーマンスが低下します。最初はすべてがスムーズに実行されているように見えますが、しばらくするとすべてが非常に遅くなり、最終的にOutOfMemoryExceptionsが発生します。奇妙なことに、タスクがプールに追加されるたびにアクティブなスレッドの数を出力すると(キューに入れられているタスクの数などの情報とともに)、スレッドプールが問題なく対応しているように見えます。プロデューサー(リスナースレッド)。

top -Hを使用してCPU使用率をチェックすると、最初は非常に均等に分散されますが、最終的にはワーカースレッドがほとんどアクティブにならず、リスナースレッドのみがアクティブになります。それでも、それ以上のタスクを送信していないようです...

誰かがこれらの症状の理由を推測できますか?複数のスレッドが追加されたときに、レガシーコード(私が制御できないもの)に何かがうまくいかない可能性が高いと思いますか?メモリ不足の問題は、どこかのキューが大きくなりすぎたために発生するはずですが、スレッドプールにキューに入れられたタスクが含まれることはほとんどないため、それは不可能です。

どんなアイデアでも大歓迎です。特に、このような状況をより効率的に診断する方法のアイデア。スレッドが実行していることなどについて、より良いプロファイルを取得するにはどうすればよいですか。

ありがとう。

4

4 に答える 4

5

速度を落とし、メモリが不足すると、メモリリークが発生します。

そこで、いくつかのJavaメモリアナライザツールを使用して、リークがあるかどうか、および何がリークされているかを特定することから始めます。運が良ければ、リークされたオブジェクトがよく知られていて、誰がすべきでないことにぶら下がっているのかがかなり明確になることがあります。

于 2011-07-26T15:22:04.917 に答える
4

回答ありがとうございます。私はJavaVisualVMを読み、それをツールとして使用しました。結果と結論の詳細を以下に示します。うまくいけば、写真は十分に長く機能します。

私は最初にプログラムを実行し、ダンプを分析して何がすべてのメモリを消費しているかを確認できると考えて、いくつかのヒープダンプを作成しました。ダンプファイルが非常に大きくなり、私のワークステーションがそれにアクセスしようとして使用が制限されていた場合を除いて、これはおそらく機能していました。1回の手術を2時間待った後、私はこれができないことに気づきました。

それで、私の次の選択肢は、私が愚かなことに、考えもしなかったものでした。アプリケーションに送信されるメッセージの数を減らすことができれば、メモリ使用量が増える傾向は続くはずです。また、ダンプファイルは小さくなり、分析が高速になります。

より遅い速度でメッセージを送信する場合、メモリ不足の問題は発生しなかったことがわかりました。メモリー使用量のグラフを以下に示します。

遅い送信

ピークは累積メモリ割り当ての結果であり、後続のトラフはガベージコレクタが実行された後です。メモリ使用量は確かに非常に憂慮すべきものであり、おそらくそこに問題がありますが、メモリリークの長期的な傾向は観察できません。

アプリケーションが壁にぶつかる場所を確認するために、1秒あたりに送信されるメッセージの割合を段階的に増やし始めました。下の画像は、前のシナリオとは非常に異なるシナリオを示しています...

高速送信

これは送信されるメッセージの割合が増えると発生するため、リスナースレッドを解放すると、大量のメッセージを非常に迅速に受け入れることができるようになり、割り当てが増えると思います。ガベージコレクターが実行されず、メモリ使用量が壁にぶつかります。

もちろん、この問題にはまだまだ多くのことがありますが、今日私が見つけたものを考えると、私はここからどこに行くべきかについてかなり良い考えを持っています。もちろん、追加の提案/コメントは大歓迎です。

この質問は、おそらくスレッドプールではなくメモリ使用量を扱うものとして再分類する必要があります...スレッドプールはまったく問題ではありませんでした。

于 2011-07-27T12:16:58.907 に答える
2

@djnaに同意します。Java同時実行パッケージのスレッドプールが機能します。スレッドが必要ない場合は、スレッドを作成しません。スレッドの数が予想どおりであることがわかります。これは、おそらくレガシーコードの何かがマルチスレッドの準備ができていないことを意味します。たとえば、一部のコードフラグメントは同期されていません。その結果、一部の要素はコレクションから削除されません。または、いくつかの追加要素がコレクションに保存されます。そのため、メモリ使用量は増加しています。

ところで、アプリケーションのどの部分がスレッドプールを使用しているのか正確にはわかりませんでした。イベントを処理するスレッドが1つあり、これを実行するスレッドがいくつかありますか?おそらくスレッド間通信メカニズムを変更しましたか?キューを追加しましたか?これはあなたの調査のさらに別の方向かもしれません。

幸運を!

于 2011-07-26T15:32:42.233 に答える
1

djnaが述べたように、ある種のメモリリークが発生している可能性があります。私の推測では、あなたはどこかでリクエストへの参照を保持していると思います:

  • リクエストをキューに入れているディスパッチャスレッドで
  • リクエストを処理するスレッドで
  • リクエストを処理しているブラックボックス内
  • ディスクに書き込むライタースレッド。

スレッドプールをミックスに追加する前にすべてがうまくいくと言ったので、プール内のスレッドはどこかにリクエストへの参照を保持していると思います。スレッドプールがないと、スレッドを再利用しないため、情報が失われるという考えです。

djnaが推奨するように、Javaメモリアナライザーを使用して、データがどこにスタックしているかを把握するのに役立てることができます。

于 2011-07-26T15:30:38.210 に答える