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でのこのポイントの例

推薦する

HTML テーブルタグチュートリアル (46): テーブルフッタータグ

<tfoot> タグは、テーブル フッターのスタイルを定義するために使用されます。基本構...

JS の Promise に中止関数を追加する方法

目次概要プロミスレースメソッド約束の再パッケージ化中止コントローラAxiosプラグインにはキャンセル...

MySQL 圧縮パッケージ版 zip インストール設定方法

圧縮版の記事ではデータの初期化がされていないなどいくつか問題があったため、Windows にインスト...

H5でクリックされたときにaタグの背景色をキャンセルする方法

1. モバイル端末でクリックされたときにタグの青色を解除する { -webkit-tap-highl...

HTMLの行間設定方法と問題点

<p></p> の行間隔を設定するには、style="line-h...

nginx.conf のルートディレクトリ設定の詳細な説明

nginx.conf を構成するときには常に何らかの問題が発生します。ここでは、よくある問題とその解...

テーブルを動的に読み込み、削除する JavaScript

この記事では、テーブルを動的に読み込み、削除するためのJavaScriptの具体的なコードを参考まで...

MySql5.7.18 の文字セット構成の詳細なグラフィック説明

背景:かなり前(2017.6.5、記事にはタイムリーさがあり、特に使用されているツールは頻繁に更新さ...

テーブル関連の配置とJavascript操作テーブル、tr、td

適切に機能するテーブル プロパティ設定:コードをコピーコードは次のとおりです。 <テーブル セ...

UDP DUP タイムアウト UPD ポート状態検出コード例

以前、単純な UDP サーバーとクライアントの例を書きましたが、その中で、自分自身をクライアントと見...

Mysql一時テーブルの原理と作成方法の分析

この記事は主にMysql一時テーブルの原理と作成方法を紹介します。この記事のサンプルコードは非常に詳...

Vueの使用に関する深い理解

目次Vueのコアコンセプトを理解するVueの双方向バインディングの原理と実装を探るVue 双方向バイ...

Dockerfile を使用して Docker でイメージを構築する方法

イメージを構築するこれまで、テストやデモンストレーションにさまざまなイメージを使用しました。多くの場...

Linux でアップロードされたファイルのスケジュールされたバックアップと増分バックアップを実装する方法

導入Alibaba Cloud のような OSS ストレージ サービスを使用している場合は、サービス...

Linuxのdateコマンドの使用

1. コマンドの紹介date コマンドは、現在の時刻または指定された時刻を指定された形式で表示するた...