Tomcatの自動シャットダウンに関するバグ修正

Tomcatの自動シャットダウンに関するバグ修正

序文

最近、4 年間実行されている Java EE Web プロジェクトでは、システムが開けないというフィードバックを顧客から頻繁に受けています。サービスを確認するためにサーバーにログインしたところ、Tomcat が自動的にシャットダウンされていることがわかりました。基本的には3〜4日ごとに発生します。

運用および保守担当者は当初、他のサービスが Tomcat サービスを強制終了したと考え、深刻に受け止めませんでした。解決策は、Tomcat を直接再起動することでした。

結局、事態は手に負えなくなり、運用・保守担当者は顧客から苦情を受け、1か月分の業績給を差し引かれました。

このバグの解決策は、紆余曲折を経て思いつきました。タスクを受け取ったら、あとは作業するだけです。解決できないバグはありません。

システムの動作環境は次のとおりです。

  • トムキャット6.0
  • 32 ビット JDK 7.0
  • Windows Server2003 32 ビット、32G メモリ。

ログを確認してください。Tomcat がクラッシュした場合、Tomcat の bin ディレクトリに「hs_err」で始まるログ ファイルが生成されます。最新のログ ファイルを開くと、最初に次の段落が表示されます。

# Java Runtime Environment を続行するにはメモリが不足しています。
# ネイティブ メモリ割り当て (malloc) が ChunkPool::allocate に 32756 バイトを割り当てることができませんでした
# 考えられる理由:
# システムの物理RAMまたはスワップ領域が不足しています
# 32ビットモードでは、プロセスサイズの制限に達しました
# 考えられる解決策:
# システムのメモリ負荷を軽減
# 物理メモリまたはスワップ領域を増やす
# スワップバッキングストアがいっぱいかどうか確認する
# 64 ビット OS で 64 ビット Java を使用する
# Java ヒープサイズを減らす (-Xmx/-Xms)
# Javaスレッドの数を減らす
# Java スレッドのスタック サイズを減らす (-Xss)
# -XX:ReservedCodeCacheSize= でより大きなコードキャッシュを設定します
# この出力ファイルは切り捨てられているか不完全である可能性があります。
#
# メモリ不足エラー (allocation.cpp:211)、pid=7864、tid=6556
#
# JRE バージョン: Java(TM) SE ランタイム環境 (7.0_79-b15) (ビルド 1.7.0_79-b15)
# Java VM: Java HotSpot(TM) Server VM (24.79-b02 混合モード windows-x86)
# コアダンプの書き込みに失敗しました。

おそらく、32756 バイトのスペースを割り当てるのに十分なメモリがないことを意味します。解決策をいくつか紹介します。

1. システムメモリの負荷を軽減します。

2. 物理メモリまたはスワップ領域を増やす。

3. 64 ビット オペレーティング システムで 64 ビット JDK を使用します。

4. Java ヒープ サイズを減らします。

5. Java スレッドの数を減らします。

6. Java スレッドのスタック サイズを減らします。

上記の内容から、jvm は 32756 バイトのメモリ領域を割り当てることができないと結論付けることができます。

タスクを受け取った瞬間から、メモリ不足の原因は JVM 構成エラーであり、新世代と旧世代の構成を調整するだけでよいと考えていました。

ログ ファイルを確認し続けて、「GC ヒープ履歴 (10 イベント):」という行を見つけます。この行には、JVM の過去 10 回のガベージ コレクション中にヒープに加えられた変更が記録されています。

GC ヒープ履歴 (10 イベント):
イベント: 572312.299 GCヒープ前
{GC 呼び出し前のヒープ = 5046 (フル 357):
PSYoungGen 合計 201472K、使用済み 200685K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 198144K、100% 使用済み [0x573c0000,0x63540000,0x63540000)
スペース 3328K から、76% 使用済み [0x63540000,0x637bb528,0x63880000)
スペース 3328K、使用率 0% [0x63880000,0x63880000,0x63bc0000)
ParOldGen 合計 843776K、使用済み 422602K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d872b18,0x573c0000)
PSPermGen 合計 262144K、使用済み 51848K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e62138,0x13bc0000)
イベント: 572312.305 GCヒープ後
GC 呼び出し後のヒープ = 5046 (フル 357):
PSYoungGen 合計 201472K、使用済み 1103K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 198144K、使用率 0% [0x573c0000,0x573c0000,0x63540000)
スペース 3328K から、33% 使用済み [0x63880000,0x63993c90,0x63bc0000)
スペース 3328K、使用率 0% [0x63540000,0x63540000,0x63880000)
ParOldGen 合計 843776K、使用済み 423618K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d970b18,0x573c0000)
PSPermGen 合計 262144K、使用済み 51848K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e62138,0x13bc0000)
}
イベント: 572351.132 GCヒープ前
{GC 呼び出し前のヒープ = 5047 (フル 357):
PSYoungGen 合計 201472K、使用済み 199247K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 198144K、100% 使用済み [0x573c0000,0x63540000,0x63540000)
スペース 3328K から、33% 使用済み [0x63880000,0x63993c90,0x63bc0000)
スペース 3328K、使用率 0% [0x63540000,0x63540000,0x63880000)
ParOldGen 合計 843776K、使用済み 423618K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d970b18,0x573c0000)
PSPermGen 合計 262144K、使用済み 51848K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e62138,0x13bc0000)
イベント: 572351.137 GCヒープ後
GC 呼び出し後のヒープ = 5047 (フル 357):
PSYoungGen 合計 201472K、使用済み 1615K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 198144K、使用率 0% [0x573c0000,0x573c0000,0x63540000)
スペース 3328K から、48% 使用済み [0x63540000,0x636d3ec8,0x63880000)
スペース 3328K、使用率 0% [0x63880000,0x63880000,0x63bc0000)
ParOldGen 合計 843776K、使用済み 423674K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d97eb18,0x573c0000)
PSPermGen 合計 262144K、使用済み 51848K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e62138,0x13bc0000)
}
イベント: 572398.649 GCヒープ前
{GC 呼び出し前のヒープ = 5048 (フル 357):
PSYoungGen 合計 201472K、使用済み 199759K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 198144K、100% 使用済み [0x573c0000,0x63540000,0x63540000)
スペース 3328K から、48% 使用済み [0x63540000,0x636d3ec8,0x63880000)
スペース 3328K、使用率 0% [0x63880000,0x63880000,0x63bc0000)
ParOldGen 合計 843776K、使用済み 423674K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d97eb18,0x573c0000)
PSPermGen 合計 262144K、使用済み 51848K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e62138,0x13bc0000)
イベント: 572398.655 GCヒープ後
GC 呼び出し後のヒープ = 5048 (フル 357):
PSYoungGen 合計 201472K、使用済み 1998K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 198144K、使用率 0% [0x573c0000,0x573c0000,0x63540000)
スペース 3328K から、60% 使用済み [0x63880000,0x63a73830,0x63bc0000)
スペース 3328K、使用率 0% [0x63540000,0x63540000,0x63880000)
ParOldGen 合計 843776K、使用済み 423703K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d985cc0,0x573c0000)
PSPermGen 合計 262144K、使用済み 51848K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e62138,0x13bc0000)
}
イベント: 576881.689 GCヒープ前
{GC 呼び出し前のヒープ = 5049 (フル 357):
PSYoungGen 合計 201472K、使用済み 200142K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 198144K、100% 使用済み [0x573c0000,0x63540000,0x63540000)
スペース 3328K から、60% 使用済み [0x63880000,0x63a73830,0x63bc0000)
スペース 3328K、使用率 0% [0x63540000,0x63540000,0x63880000)
ParOldGen 合計 843776K、使用済み 423703K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d985cc0,0x573c0000)
PSPermGen 合計 262144K、使用済み 51850K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e62850,0x13bc0000)
イベント: 576881.696 GCヒープ後
GC 呼び出し後のヒープ = 5049 (フル 357):
PSYoungGen 合計 201472K、使用済み 3155K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 198144K、使用率 0% [0x573c0000,0x573c0000,0x63540000)
スペース 3328K から、94% 使用済み [0x63540000,0x63854cb0,0x63880000)
スペース 3328K、使用率 0% [0x63880000,0x63880000,0x63bc0000)
ParOldGen 合計 843776K、使用済み 423703K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d985cc0,0x573c0000)
PSPermGen 合計 262144K、使用済み 51850K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e62850,0x13bc0000)
}
イベント: 580535.452 GCヒープ前
{GC 呼び出し前のヒープ = 5050 (フル 357):
PSYoungGen 合計 201472K、使用済み 201299K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 198144K、100% 使用済み [0x573c0000,0x63540000,0x63540000)
スペース 3328K から、94% 使用済み [0x63540000,0x63854cb0,0x63880000)
スペース 3328K、使用率 0% [0x63880000,0x63880000,0x63bc0000)
ParOldGen 合計 843776K、使用済み 423703K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d985cc0,0x573c0000)
PSPermGen 合計 262144K、使用済み 51856K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e64228,0x13bc0000)
イベント: 580535.459 GCヒープ後
GC 呼び出し後のヒープ = 5050 (フル 357):
PSYoungGen 合計 200960K、使用済み 1858K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 197632K、使用率 0% [0x573c0000,0x573c0000,0x634c0000)
スペース 3328K から、55% 使用済み [0x63880000,0x63a50be0,0x63bc0000)
スペース 3584K、使用率 0% [0x634c0000,0x634c0000,0x63840000)
ParOldGen 合計 843776K、使用済み 423703K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d985cc0,0x573c0000)
PSPermGen 合計 262144K、使用済み 51856K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e64228,0x13bc0000)
}

上記の内容を読んだ後、Tomcat フラッシュ クラッシュが、古い世代、永続的な世代、および新しい世代のスペース不足によって発生したとはわかりませんでした。 Eden 領域の使用率が 100% に達したためにフル GC が数回発生しましたが、ガベージ コレクション後、Eden 領域のスペースは通常のレベルに戻りました。

ログには、Tomcat がクラッシュしたときのヒープ使用量も記録されます。

ヒープ
PSYoungGen 合計 200960K、使用済み 95671K [0x573c0000、0x63bc0000、0x63bc0000)
エデン スペース 197632K、47% 使用済み [0x573c0000,0x5cf5d230,0x634c0000)
スペース 3328K から、55% 使用済み [0x63880000,0x63a50be0,0x63bc0000)
スペース 3584K、使用率 0% [0x634c0000,0x634c0000,0x63840000)
ParOldGen 合計 843776K、使用済み 423703K [0x23bc0000、0x573c0000、0x573c0000)
オブジェクト スペース 843776K、50% 使用済み [0x23bc0000,0x3d985cc0,0x573c0000)
PSPermGen 合計 262144K、使用済み 51856K [0x03bc0000、0x13bc0000、0x23bc0000)
オブジェクト スペース 262144K、19% 使用済み [0x03bc0000,0x06e64228,0x13bc0000)

すべてがとても普通でしたが、同時にとても奇妙でした。

過去のログを確認しましたが、内容は似ていました。

私はログを数回読み直し、今回はログに提案されている解決策に焦点を当てました。

# システムのメモリ負荷を軽減
# 物理メモリまたはスワップ領域を増やす
# スワップバッキングストアがいっぱいかどうか確認する
# 64 ビット OS で 64 ビット Java を使用する
# Java ヒープサイズを減らす (-Xmx/-Xms)
# Javaスレッドの数を減らす
# Java スレッドのスタック サイズを減らす (-Xss)

以下の解決策は採用されません。

  • システムのメモリ負荷を軽減します。 システムメモリは十分です。メモリは 32​​G あり、20G は未使用です。メモリを減らす必要はありません。
  • 物理メモリまたはスワップ領域を増やします。 システムメモリは十分です。メモリは 32​​G あり、20G は未使用です。物理メモリを増やす必要はありません。
  • 64 ビット OS では 64 ビット Java を使用します。 32 ビット オペレーティング システム、64 ビット JDK は使用できません。

残る解決策は次の 3 つだけです。

  • Java ヒープ サイズを減らします (-Xmx/-Xms)。ヒープが大きすぎると、残りのメモリに影響します。
  • Javaスレッドの数を減らす
  • Java スレッドのスタック サイズを減らす (-Xss)

Java スレッドの数を減らすにはコードを変更する必要があり、これも現実的ではありません。

最後に、

  • Java ヒープ サイズを減らす (-Xmx/-Xms)
  • Java スレッドのスタック サイズを減らす (-Xss)

これらが2つの解決策です。ここから始めれば、前途に光が見えてきます。

Javaスレッドスタックサイズを減らす(-Xss)ソリューションの初見

Java スレッドを実行するにはメモリ領域も必要です。-Xss パラメータは、各スレッド スタックのサイズと、JVM によって開始された各スレッドに割り当てられるメモリ サイズを指定します。 JDK 1.4 では 256K、JDK 1.5 以上では 1M です。

tomcat jvm のパラメータは次のように設定されます。

JAVA_OPTS=%JAVA_OPTS% -server -Xms1024m -Xmx1024m -Xmn200M -XX:PermSize=256M -XX:MaxPermSize=512m -XX:SurvivorRatio=1 -Xss256k

各 Java スレッドのスタック サイズは、-Xss によって 256K に設定されています。

Java 言語では、スレッドを作成すると、仮想マシンは JVM メモリ内に Thread オブジェクトを作成し、同時にオペレーティング システム スレッドを作成します。このシステム スレッドのメモリは JVMMemory ではなく、システム内の残りのメモリ (MaxProcessMemory - JVMMemory - ReservedOsMemory) です。

スレッドを作成する必要があり、オペレーティング システムの残りのメモリが Java スレッドに割り当てるのに不十分な場合は、メモリ不足エラーが報告されます。

Java スレッド スタック サイズは -Xss によって 256K に設定されているため、このソリューションは使用しないことに決定されました。

残された唯一の解決策は、Java ヒープ サイズを減らす (-Xmx/-Xms) ことです。ヒープ サイズを縮小すると、Java スレッド スタックに十分なメモリ領域が確保されます。

32 ビット Windows オペレーティング システムでは、ヒープ最大容量と PermSize の最大容量を差し引いた 2G のメモリ領域が各プロセスに割り当てられ、残りの容量は Java スレッド スタック用に予約されます。

コードと以前のエラー ログを分析した結果、メモリ不足エラーは通常 350 スレッド付近で発生することがわかりました。
エラー発生時点では、ヒープ領域の 40% 未満が使用されていました。そのため、Java ヒープを 1G から 768M に削減することが決定されました。

変更された jvm パラメータは次のとおりです。

JAVA_OPTS=%JAVA_OPTS% -server -Xms768m -Xmx768m -Xmn200M -XX:PermSize=256M -XX:MaxPermSize=512m -XX:SurvivorRatio=1 -Xss256k

これまでのところ、システムは 1 か月間安定して稼働しており、すべてのパラメータ指標は正常範囲内です。最大ヒープ使用量はわずか 70% です。

要約:

1. 今回のバグを解決した後、Java 仮想マシン、特にスレッド スタック、メモリ ヒープ、永続世代、新規世代などの概念についての理解が深まりました。

2. ログ ファイルを注意深く読み、潜在的な解決策を段階的に排除してください。総合的なシステムの動作環境を考慮し、適切な解決策を見つけます。

さて、今回の記事は以上です。この記事の内容が皆さんの勉強や仕事に少しでも参考になれば幸いです。123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Asp.net で Tomcat の起動とシャットダウンを制御する方法
  • Shutdown.batを使用してTomcatをシャットダウンすると他のTomcatもシャットダウンしてしまう問題を解決します
  • JavaでTomcatサーバーを起動/停止する方法
  • JavaコードはTomcatプログラムを閉じて問題を分析します

<<:  case when文のエラー問題の詳細な説明

>>:  関数の分類の詳細な説明とJavascriptでのこのポイントの例

推薦する

MySQL設定ファイルを変更できない問題の解決方法(Win10)

他の人のために解決した問題を記録します。問題の説明MySQLのバージョンは5.7、オペレーティングシ...

Nginx サーバーで URL リンクを設定する方法

LNMPのようなアーキテクチャを持つウェブサイトは、一般的にPHPフレームワークに基づいて開発されて...

Oracle Rownum 書き込みに似た MySQL の詳細な例

Rownum は、Oracle での独自の書き込み方法です。Oracle では、rownum を使用...

MySQL デッドロック ルーチン: 一意のインデックスの下でのバッチ挿入順序の不一致

序文デッドロックの本質はリソースの競合です。バッチ挿入の順序が一貫していないと、デッドロックに陥りや...

MySQL における int の最大値の詳細な説明

導入2日前に見た問題について詳細に書きます。バイトコンピューターがバイナリに基づいていることは誰もが...

JavaScript でよく使われる 5 つのオブジェクト

目次1. JavaScript オブジェクト1).配列オブジェクト2).ブールオブジェクト3).日付...

プロジェクトの再構築からプロジェクトにおける CSS3 カスタム変数の使用について話す

CSS3変数について変数を宣言するときは、変数名の前に 2 つのハイフン ( -- ) を追加します...

MySQL におけるユニーク制約と NULL の詳細な説明

序文説明を簡略化するために以前に設定した要件は、他のグループから MQ メッセージを受信し、データベ...

CSS パフォーマンスの最適化 - will-change の使用方法の詳細な説明

will-change は、要素にどのような変更が行われるかをブラウザに伝え、ブラウザが事前に最適化...

MySQLデータベースのマスタースレーブ同期の実際のプロセスの詳細な説明

目次インストール環境の説明MySQLデータベースサービスをインストールするメインライブラリを構成する...

JS配列ループ方式と効率分析の比較

配列メソッドJavaScript には多くの配列メソッドが用意されています。次の図は、ほとんどの配列...

axios を使用してプロジェクト内の複数の繰り返しリクエストをフィルタリングする方法

目次1. はじめに:この場合、通常は 2 つのアプローチがあります。 2. CancelToken ...

CSS で中空マスク レイヤーを実装するサンプル コード

この記事の内容: ページ中空マスクレイヤー、ページ中空マスクガイドレイヤー、画像中空マスク通常のマス...

Nginxを使用してストリーミングメディアサーバーを構築し、ライブブロードキャスト機能を実現する

前面に書かれた近年、ライブストリーミング業界は非常に人気が高まっています。伝統的な業界でのライブスト...

jsはシンプルなカウントダウンを実装します

この記事の例では、参考までに簡単なカウントダウンを実装するためのjsの具体的なコードを共有しています...