24

私は非常に独特な問題に直面しています。私のTomcatは24時間年中無休で約25%のCPUで完全に動作しますが、ある日、私のCPUは最大60%になり、システムは停止して回復に失敗します。

スローダウン中にスレッドダンプを取得すると、ほとんどすべてのスレッドが何らかの文字列または関連する操作でビジー状態になります。

OutOfMemoryエラーや例外がスローされることはなく、すべての要求は引き続き処理されますが、応答時間はn度まで低下し、1秒未満の要求でも60秒以上かかるようになります。

私のサーバー構成は次のとおりです。

    Ubuntu 12.04.2 LTS
    Linux 3.2.0-38-仮想#60-Ubuntu SMP x86_64 x86_64 x86_64 GNU / Linux
    Javaバージョン「1.7.0_13」
    Java(TM)SEランタイム環境(ビルド1.7.0_13-b20)
    Java HotSpot(TM)64ビットサーバーVM(ビルド23.7-b01、混合モード)
    エクスポートJAVA_OPTS='-サーバー
    -Xms18g -Xmx18g
    -XX:MaxPermSize = 512m
    -XX:ThreadStackSize = 512
    -XX:NewRatio = 1
    -XX:SurvivorRatio = 4
    -XX:+ UseConcMarkSweepGC
    -XX:+ UseParNewGC
    -XX:+ CMSClassUnloadingEnabled
    -Xloggc:/usr/tomcat/logs/gc.log
    -XX:+ PrintGCDetails
    -XX:+ PrintGCDateStamps
    -XX:+ PrintTenuringDistribution
    -Dcom.sun.management.jmxremote
    -Dcom.sun.management.jmxremote.port = 9999
    -Dcom.sun.management.jmxremote.authenticate = false
    -Dcom.sun.management.jmxremote.ssl = false
    -Djava.awt.headless = true '

スレッドダンプをダウンロードするには、ここをクリックしてください。スレッドとそのスタックトレースの大部分を削除しました

vmstatログをダウンロードするには、ここをクリックしてください

gcログをダウンロードするには、ここをクリックしてください

これの原因について何か考えはありますか?ありがとう

4

9 に答える 9

3

問題のあるリクエストを特定するために、Tomcatでスタックスレッド検出バルブを設定できます。

このバルブを使用すると、処理に時間がかかる要求を検出できます。これは、処理しているスレッドがスタックしていることを示している可能性があります。

このような要求が検出されると、そのスレッドの現在のスタックトレースがWARNレベルでTomcatログに書き込まれます。

スタックスレッドのIDと名前は、JMXのstuckThreadIds属性とstuckThreadNames属性で利用できます。IDを標準のスレッドJVMMBean(java.lang:type = Threading)で使用して、スタックした各スレッドに関するその他の情報を取得できます。

于 2013-02-26T14:43:47.817 に答える
3

CPU使用率が100%を下回っていても、アプリが停止した場合、これは、CPUが完全に使用されていない原因が何かにあることを意味します。

I / Oまたは過度のコンテキスト切り替え(たとえば、ロックによって引き起こされる)は、これの通常の原因です。

イベントの1つでvmsstat1からの出力を投稿できますか?-診断の次のステップは、コンテキストスイッチングがここでの問題であるかどうかを明確にすることです。

于 2013-11-23T14:20:50.947 に答える
3

ダンプではGCがビジーでもなく、十分なメモリが使用可能であるため、これはメモリの問題ではありません。さらに、CPUは60%でスタックしますが、アプリケーションが計算でビジー状態になる場合(GCなど)、これがネットワーク攻撃の場合と同じように、100%でスタックします。したがって、この問題の原因には、ディスクIO操作が含まれている必要があります。

Tomcatはバグがあることが知られており、いくつかの重大な問題があります。私が遭遇したことの1つは、特別な理由もなく、Tomcatが突然自身のログファイルをナンセンスエントリで溢れさせたことです。これにより、ディスクが100%までいっぱいになるだけでなく、着信要求の速度が大幅に低下しました。これは、Tomcatログとそのサイズを確認することで確認できます。

これがソースでない場合は、利用可能なツールを使用して、Tomcatの奇妙なディスクIOをチェックし、そこから続行する必要があります。

于 2013-11-23T14:47:40.973 に答える
3

あなたの問題は-XX:PermSize=320m -XX:MaxPermSize=320m、PemSpaceを動的に変更できないようにすることによるこの構成の決定であると思います。それを使い果たすと、デッドロックが発生します。インターンキャッシュはPermSpaceを使用することを忘れないでください。に変更-XX:MaxPermSize=320mしてみ-XX:MaxPermSize=512mます。

于 2013-11-29T05:31:36.970 に答える
3

次のJVMオプションを使用して、コードキャッシュの最大サイズを増やしてみてください。

-XX:ReservedCodeCacheSize=256m

この提案の背景については、別の質問に対する私の回答を参照してください。

于 2014-11-05T17:54:07.077 に答える
2

GCログに異常はありますか?いくつかの珍しいオプションを備えたかなり大きなヒープで実行していて、文字列の割り当てをたくさん行っているようです。たぶん、時間の経過とともにヒープの断片化に悩まされるでしょう(CMSは圧縮されません)。また、スワッピングが行われていないことを確認します(ヒープが大きすぎる場合に発生する可能性があるため、VMがアクセスすることはめったにありません)

明らかにスレッドがブロックされていないので、これはGCに関連していると思われます。最近のJDKを試してみましたか?また、再試行することもできますが、JDKのマイナーリリースごとにテストカバレッジがあまりない可能性があるため、やや珍しいオプション-XX:+CMSScavengeBeforeRemarkを削除します。

もう1つの疑いは、奇妙な文字セット(キリル文字またはアラビア語)を使用した着信要求であり、大量の文字セットマッピングのオーバーヘッドが発生する可能性があります。また、ページにロボットがあるかどうか、疑わしいリクエストが入っていないかどうかを確認してください。文字列操作のルート操作を見つけるには、間違いなく長いスタックトレースが必要です。

于 2013-11-23T23:44:20.653 に答える
1

メソッド呼び出しを診断するBTraceを使用する必要があります。

次のようなbreaceスクリプトを記述します。

com.xx.xxStringのanyメソッドを呼び出すプレフィックスクラスをトレースし、呼び出し時間を出力します。

@TLS
private static Map<String, Integer> countMap = BTraceUtils.newHashMap();

private static String prefix = "com.xx.xx";// package like com.xx.xx which you want to trace ()

@OnMethod(clazz = "java.lang.String", method = "/.*/") //all method in String
public static void traceMethodInvoke() {
    String str = BTraceUtils.jstackStr();
    for (String currentClass : str.split("\\n")) {
        if (BTraceUtils.Strings.startsWith(currentClass, prefix)) {
            if (!countMap.containsKey(currentClass)) {
                countMap.put(currentClass, 1);
            } else {
                countMap.put(currentClass, countMap.get(currentClass) + 1);
            }
            break;
        }
    }
}

@OnTimer(5000)
public static void print() {
    BTraceUtils.println("========================================");
    for (Map.Entry<String, Integer> entry : countMap.entrySet()) {
        if (entry.getValue() > 100) {// print if cont > 10
            BTraceUtils.println(entry.getValue() + "\t\t" + entry.getKey());
        }
    }
    BTraceUtils.println("===========================================");

}  

結果は次のように出力されます。

====================================================
1022                           com.xx.xx.classA#m1
322                            com.xx.xx.classA#m2
2022                           com.xx.xx.classA#m21
422                            com.xx.xx.ccc.classX#m11
522                            com.xx.xx.zz.classS#m44
.........

prefix別のパッケージプレフィックスをトレースするためにを変更できます。

その結果、ソースコードを分析して問題点を見つけることができます。

于 2013-11-26T16:23:17.840 に答える
1

RUNNABLEスレッドを調べてスレッドダンプをスキャンしたところ、1つの点が際立っています。システムが同時に多数のリクエストを処理/処理しようとしているようです。また、コアの数が少ない場合を除いて、スライスに多くの時間がかかる可能性があります。一方、これがGCに関連しているという明確な>>証拠<<はわかりません。(ただし、GCログは含めませんでした...)

私はあなたが2つのことを見ることを提案します。

  • オペレーティングシステムの仮想メモリ統計を確認します。壊滅的なシステム速度低下の考えられる原因の1つは、仮想メモリのスラッシングです。これは、仮想メモリページに対するアプリケーションの総需要が利用可能な物理メモリを超える場合です...そしてオペレーティングシステムは、物理メモリとスワップディスク/ページファイルの間でページを交換するのに多くの時間を費やします。
  • 受け取っているリクエストのパターンを見てください。特定の時間に、取得するリクエストの数/タイプが単にシステムの容量を超えている可能性があります。

問題がVMスラッシングである場合、解決策はアプリケーションのメモリ需要を減らすことです。これを行う簡単な方法は、Javaヒープサイズを減らすことです。

問題が負荷である場合、それを解決するのは困難です。

  • ハードウェアを強化する(または仮想にVCPUを追加する)ことを試みることができます。
  • サーバーの複数のインスタンスに負荷を分散してみることができます。
  • サーバーが一度にそれほど多くのリクエストを処理しようとしないように、ワーカースレッドの数を減らすことを試みることができます。
  • アプリケーションのプロファイリングやリクエスト統計の分析を試して、調整可能なホットスポットや、オフにできる高価な機能があるかどうかを確認できます...

最後に、CMSからParallelCollectorに切り替えることが役立つかどうかを確認できます。Oracle GC Tuningページ:AvailableCollectorsを参照してください。しかし、これがGCの問題だとは思えません。

于 2014-03-24T16:18:36.877 に答える
0

最初に実行する必要があるのは、実際にCPUを消費しているスレッドを見つけることです。文字列操作を実行するスレッドの場合もあれば、GCおよびスイープ操作を実行する他のVMスレッドの場合もあります。リンクは、CPUスパイクをスレッドダンプと相互に関連付ける方法を示しています

スレッドを正確に特定できれば、次のステップで何をすべきかがより明確になります。

お役に立てれば

于 2013-02-01T17:15:12.750 に答える