7

スタンドアロン環境でSpring3.1を使用しています。

(この問題はSpringに関連する必要はありません。スタンドアロン環境でも同じように動作します。)

トピックからメッセージを受信するリスナーを実装しました。メッセージレートは非常に高いです(約20/30 m / sについて話します)。

一部のメッセージは、他のメッセージよりも処理時間がかかる場合があります。

リスナーは同じインスタンスで動作します。つまり、1つのメッセージの処理時間が長すぎると、パフォーマンスにかなりの影響を及ぼします。

同じリスナーインスタンスを使用する代わりに、独自のオブジェクトプールを作成することを考えましたが、エグゼキューター(java.util.concurrent.Executors)を見つけました。

したがって、受信したメッセージごとに異なるスレッドが割り当てられます。これにより、リスナーインスタンスがメッセージを自由に並列処理できるようになります。

private ExecutorService  threadPool = Executors.newFixedThreadPool(100);
    @Override
    public void onMessage(final Message msg)
    {
        Runnable t = new Runnable()
        {
            public void run()
            {
                onSessionMessage(msg);
                log.trace("AbstractSessionBean, received messge");
            }
        };
        threadPool.execute(t);
    }

これでパフォーマンスの問題は解決したようです。しかし、jconsoleでアプリケーションを監視した後、私たちは今、巨大なメモリリークに直面しています。

ヒープメモリの使用量は、時間の経過とともに大幅に増加しています。

そこで、FixedThreadPoolのサイズ番号を少し使って「再生」しようとしました。まだ膨大なメモリ使用量があります:

ここに画像の説明を入力してください

どうすればこれを解決できますか?私の重要な問題を解決するための他のアイデアはありますか?

GBを実行した後のjconsole

jconsoleの全体図

ヒープダンプを実行した後、2つの問題の疑いがありました。

ヘッドダンプ

ありがとう、レイ。

4

4 に答える 4

4

あなたが遭遇した問題は、threadPoolがそのリソースを解放していないことだと思います。送信または実行が終了したら、threadPool.shutdown()を呼び出す必要があります。これは、タスクが完了するまで待機してから、ガベージコレクションできるスレッドを終了します。

公式のJavaAPIWebサイトから:

「未使用のExecutorServiceをシャットダウンして、リソースを再利用できるようにする必要があります。」 https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html#shutdown()

または、newCachedThreadPool()を使用して、「必要に応じて新しいスレッドを作成するスレッドプールを作成しますが、以前に作成されたスレッドが利用可能になったときに再利用します」を使用できます。https://docs.oracle.com/javase/7/docs/apiを参照してください。 /java/util/concurrent/Executors.html

この問題が発生したときは、newFixedThreadPool()とshutdownオプションを使用しました。

于 2017-05-10T11:31:04.943 に答える
2

メモリリークは問題ないと思います。少なくともjconsoleチャートからはわかりません。主要なGCコレクションはありません。したがって、tenured(old)世代に割り当てられるオブジェクトはますます増えているようです。メモリリークを確実にするには、GCを実行し、その後、割り当てられたメモリを比較する必要があります。リークを見つけた場合は、jmapまたはビジュアルツール(標準のJDKツール)を使用してヒープダ​​ンプを作成できます。この後、ヒープダンプはMATで分析できます。ヒープダンプを取得する前に、GCを実行してヒープダ​​ンプファイルのサイズを小さくすることをお勧めします。

いくつかのメモ:

  • スレッド数は、ヒープメモリに明示的に影響を与えるべきではありません。次のJavaメモリ構造を確認すると役立つ場合があります。したがって、スレッドにはヒープではなくスタックメモリが必要です。
  • 一般に、GCはアルゴリズムで動作するため、重くないオブジェクトのキャッシュを作成することはお勧めできません。
  • また、キャッシュされたスレッドプールを検討するか、サーバーハードウェアに応じてThreadPoolSizeを調整する必要があると思います。
于 2012-12-17T09:12:11.873 に答える
0

ExecutorServiceを使用した後は、スレッドプールを停止する必要があります。これは、ファイルやデータベースなど、明示的なリリースが必要なものと同じリソースであるためです。ExecutorServiceにはshutdown()メソッドとshutdownNow()メソッドがあり、finallyブロックで使用してgargabecollectを実行できます。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class SimpExec {
  public static void main(String args[]) {
    CountDownLatch countDownLatch = new CountDownLatch(5);
    CountDownLatch CountDownLatch2 = new CountDownLatch(5);

    ExecutorService eService = Executors.newFixedThreadPool(2);

    eService.execute(new MyThread(countDownLatch, "A"));
    eService.execute(new MyThread(CountDownLatch2, "B"));


    try {
      countDownLatch.await();
      CountDownLatch2.await();

    } catch (InterruptedException exc) {
      System.out.println(exc);
    }
      finally{

        eService.shutdown(); // This is the method we use to avoid memory Leaks.
        // eService.shutdownNow(); // -do-
       }
  }
}

class MyThread implements Runnable {
  String name;

  CountDownLatch latch;

  MyThread(CountDownLatch c, String n) {
    latch = c;
    name = n;
    new Thread(this);
  }

  public void run() {
    for (int i = 0; i < 5; i++) {
      latch.countDown();
    }
  }
}

シャットダウンを忘れると、メモリリークが発生します。これは、デフォルトのエグゼキュータがデーモンスレッドを作成しないため、通常はJVMによって閉じられないストリームのようなものでもあります。

于 2012-12-17T09:12:23.677 に答える
0

単なる提案ですが、1秒あたり30のメッセージを作成し、コンピューターがこれらの30のメッセージを1秒よりも処理するのに(並列でも)時間がかかる場合、送信されたタスクのキューは制御不能に増加します。キューサイズが設定された数よりも大きい場合は、タスクが送信されないことを確認して、少し待つ必要があります。各Messageオブジェクトはメモリを使用しますが、これが問題になる可能性があると思います。cachedthreadpoolはこれを解決しません。

キューのサイズを出力することで、これを非常に簡単にテストできます。これがすでに解決されているかどうかわからない...

于 2017-01-24T08:09:06.980 に答える