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 でフロートをクリアする 2 つの方法

1. クリアフローティング法1前の親要素の高さを設定します。注: エンタープライズ開発では、可能であ...

vue+openlayer をベースにマップの集約と散乱効果を実現

目次序文:結果: 1.重合効果: 2. 散布効果:具体的な実装手順: 1. プロジェクトにOpenL...

Vue Routerはバックグラウンドデータに応じて異なるコンポーネントをロードします

目次実際のプロジェクトで遭遇する要件実装が間違っているところもある私は個人的に、実装するより良い方法...

CSS でコンテンツが長すぎる問題を解決する方法の詳細な説明

CSS を記述するときに、デザインに存在する重要なケースを忘れてしまうことがあります。たとえば、コン...

MySQLシリーズのMariaDBサーバーのインストール

目次チュートリアルシリーズ1. yumパッケージマネージャーを使用してMariaDBサーバーをインス...

フロントエンド開発に必須:推奨されるブラウザ互換性テストツール 12 選

フロントエンド開発者にとって、さまざまな主要ブラウザのさまざまなバージョンでコードが適切に動作するこ...

JavaScript におけるイベント バブリング メカニズムの詳細な分析

バブリングとは何ですか? DOM イベント フローには、イベント キャプチャ ステージ、ターゲット ...

CSS+JS で水滴の波紋アニメーション ボタン効果を実装するサンプル コード

コードは次のようになります。 <!DOCTYPE html> <html lang...

入力テキストボックスの長さをコンテンツに応じて変更する方法

初め:コードをコピーコードは次のとおりです。 <input type="text&q...

react+reduxを使用してカウンター機能を実装すると発生する問題

Redux はシンプルな状態マネージャーです。その歴史をたどることはしません。使用法の観点から見ると...

Linux での UDP について学ぶ

目次1. UDPとLinuxの基礎の紹介2. 各機能の使い方1. ソケット機能の使用2. バインド機...

Docker で Oracle 11g イメージ構成をプルダウンする際の問題を分析する

1. イメージをプルするdocker pull レジストリ.cn-hangzhou.aliyuncs...

vue+element を使用した Google プラグインの開発プロセス全体

シンプルな機能: ブラウザの右上隅にあるプラグイン アイコンをクリックすると小さなポップアップ ウィ...

Linuxにおけるselinuxの基本設定チュートリアルの詳細な説明

selinux ( Security-Enhanced Linux)は、Linux カーネル モジュ...

Windows システムに mysql5.7.21 をインストールするための詳細なチュートリアル

MySQL インストーラーは、MySQL ソフトウェアのあらゆるニーズに対応する、使いやすいウィザー...