2

単一の物理ホストでアプリケーションの複数のインスタンス (Tomcat サーバーごとに 1 つ) を実行しています。アプリケーションは適切なロギングを行います。最近、一部のアプリケーションが遅くなったりハングしたりし、再起動する必要があることが確認されました。スレッド ダンプでは、ログ ステートメントですべてのスレッドがブロックされ、printlnオブジェクトのロックを待機していることがわかります。および他のオブジェクトがすでにロックを取得していますprintln。しかし、他のスレッドがprintlnオブジェクトのロックを解放しなかった理由がわかりませんでしたか? いくつかのスレッド ダンプ スナップショットを貼り付けます。

ブロックされたスレッド ダンプ:

java.lang.Thread.State: BLOCKED (on object monitor)
    at java.io.PrintStream.println(PrintStream.java:755)
    - waiting to lock <0x00000007830097e0> (a java.io.PrintStream)
    at org.apache.tomcat.util.log.SystemLogHandler.println(SystemLogHandler.java:238)
    at com.webaroo.smsnew.common.SMSUtils.log(SMSUtils.java:167)
    at com.webaroo.smsnew.common.SMSUtils.log(SMSUtils.java:113)

printlnのログを取ったスレッドのスレッドダンプ。

java.lang.Thread.State: BLOCKED (on object monitor)
    at java.nio.CharBuffer.wrap(CharBuffer.java:350)
    at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:246)
    at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:106)
    - locked <0x00000007830098f0> (a java.io.OutputStreamWriter)
    at java.io.OutputStreamWriter.write(OutputStreamWriter.java:190)
    at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:111)
    - locked <0x00000007830098f0> (a java.io.OutputStreamWriter)
    at java.io.PrintStream.write(PrintStream.java:476)
    - locked <0x00000007830097e0> (a java.io.PrintStream)
    at java.io.PrintStream.print(PrintStream.java:619)
    at java.io.PrintStream.println(PrintStream.java:756)
    - locked <0x00000007830097e0> (a java.io.PrintStream)
    at org.apache.tomcat.util.log.SystemLogHandler.println(SystemLogHandler.java:238)
4

1 に答える 1

2

ログ ステートメントですべてのスレッドがブロックされ、println オブジェクトのロックを待機していることがわかりました。他のオブジェクトが既に println をロックしています。

PrintStreamクラス である と はSystem.out、両方System.errとも同期クラスです。println(...)複数のスレッドが重複する出力行を取得しないように、メソッドは印刷を行う前にロックします。

スレッド ダンプがその場所でスレッドがブロックされていることを示しているからといって、そのスレッドがハングしているとは限りません。アプリケーションの最も遅い部分を意味するだけかもしれません。より多くのスレッド ダンプは、他のスレッドが に到達しているprintln()が、そこでブロックされていることを示します。そこで多数のスレッドがブロックされている場合、出力 IO (おそらくコンソールへの) がアプリケーションの速度を低下させています。ログ メソッドの数を減らすか、各メッセージの情報量を減らす必要があります。これで解決しない場合は、他の出力メカニズムを検討する必要があります。

出力が必要な場合は、各スレッドが独自のBufferedWriterラッピング aFileWriterなどに書き込むことができます。または、単一のスレッドが実際の出力を実行し、他のすべてのスレッドがメッセージを に追加しBlockingQueue、1 つのライターがメッセージをデキューしてから、BufferedWriterロックせずに I/O をバッファリングしない に書き込むこともできます。

private final BlockingQueue<String> messageQueue
      = new ArrayBlockingQueue<String>();
...
// add a message to the queue
messageQueue.add("some log output here: " + someValue);
...
// writer thread
private class LogThread implements Runnable {
    public void run() {
       BufferedWriter writer =
            new BufferedWriter(new FileWriter("/var/log/some_log_file.txt"));
       try {
          while (!Thread.currentThread().isInterrupted()) {
             String msg = messageQueue.take();
             writer.write(msg);
          }
       } finally {
          writer.close();
       }
    }
}

ただし、1 つのスレッドがバッファリングされたストリームを介して書き込みを行っている場合でも、ハードディスクの IO 帯域幅を超えていることに気付く場合があります。その時点で gzip されたストリームを試すことができますが、通常は出力の再評価が必要です。出力行数を減らすことはできますか? ある種のカウンターをメモリに保持し、頻繁にダンプできますか。ログ出力が本当に必要な場合は、SSD に移行してドライブの速度を上げることを検討する必要があります。

于 2013-11-03T19:54:05.980 に答える