JVM: 自分がどうやって死んだのか知りたいだけ

JVM: 自分がどうやって死んだのか知りたいだけ

[[347298]]

Java プログラムが JVM 上で実行されることは誰もが知っています。 JVM に何らかの障害が発生すると、サービスの安定性が必然的に影響を受けます。運が良ければ、サービスが不安定になり、一部のリクエストが遅延したり異常になったりする可能性があります。運が悪いと、JVM が直接クラッシュし、サービスが完全に中断されることになります。

これは良いことではありません。 JVM とともに、サービスだけでなく私たちの精神も崩壊するでしょう。

いわゆる JVM クラッシュは、一般的にメモリ オーバーフロー、つまり OutOfMemoryError と StackOverflowError を指します。もう 1 つの状況は、オフヒープ メモリの使用量が非常に大きい場合です。これにより、JVM が配置されているマシンのメモリが膨張し、マシンの再起動などの異常な状況が発生します。この状況をメモリ リークと呼びます。

では、どのような状況で JVM がクラッシュするのでしょうか?クラッシュにはどのような種類がありますか?諺にあるように、自分と敵を知ることによってのみ、百戦錬磨の勝者となることができるのです。クラッシュの原因を理解することによってのみ、JVM クラッシュの問題をより適切に解決できます。

まず、JVM メモリ モデル図を示します。 JVM は理解するのが非常に抽象的です。次の図は、JVM メモリ モデルを具体的に理解するのに役立ち、オーバーフローが発生する部分も図で確​​認できます。 JDK 8 では、永続世代は存在しなくなり、メタスペースに置き換えられました。

次に、Hotspot JDK 8 を背景として、JVM メモリ オーバーフローとメモリ リークのいくつかのケースを見ていきます。

まず、ヒープ領域のサイズを制限するために JVM 起動パラメータを設定します。ヒープ領域は、新世代用に 10M、メタスペース用に 10M を含む 20M に設定され、ガベージ コレクション アルゴリズムとして CMS アルゴリズムを使用するように指定します。以降のすべての例では、このパラメータ セットを使用します。

  1. -XX:+ConcMarkSweepGC を使用する
  2. -XX:+CMS 占有開始のみを使用する
  3. -XX:CMS開始占有率=70
  4. -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
  5. -XX:+CMSクラスのアンロードが有効
  6. -XX:+並列参照プロセス有効
  7. -XX:+CMSScavengeBeforeRemark
  8. -詳細:gc
  9. -Xms20M
  10. -Xmx20M
  11. -Xmn10M
  12. -XX:+GC詳細を印刷
  13. -XX:生存率=8
  14. -XX:+メモリ不足エラー時のヒープダンプ
  15. -XX:メタスペースサイズ=10M
  16. -XX:最大メタスペースサイズ=10M
  17. -XX:HeapDumpPath=/Users/fengzheng/jvmlog

ヒープオーバーフロー

ヒープ オーバーフローは、おそらく最も一般的なメモリ オーバーフローのシナリオです。 JVM に割り当てられたほとんどのオブジェクト インスタンスと配列はヒープ上に格納されます。さらに、ヒープメモリはガベージコレクターの主戦場でもあります。

Java プログラムが起動すると、ヒープ領域のサイズが指定されます。新しいオブジェクトと配列が作成されると、それらはヒープ上に割り当てられます。新しいオブジェクトがスペースを要求したときに、ヒープ メモリが不足している場合は、ガベージ コレクションが発生します。ほとんどの場合、これはマイナー GC と呼ばれる新しい世代で発生します。新しい世代が収集されてもまだ十分なスペースがない場合は、FullGC が発生します。 FullGC後も領域が不足している場合は、OOMエラー(ヒープオーバーフロー)が発生します。

このシナリオをシミュレートする

  1. プライベートファイナルスタティック 整数_1K = 1024;
  2.  
  3. 公共 静的void main(String[] args){
  4. リスト<byte[]> byteList = 新しいArrayList<>();
  5. 静かにクラッシュヒープを待機します(byteList);
  6. }
  7.  
  8. 公共 静的void quietlyWaitingForCrashHeap(List<byte[]> byteList) {
  9. 試す {
  10. )の間{
  11. byteList.add (新しいバイト[500 * _1K]);
  12. //スレッドスリープ(1000);
  13. スレッド.sleep(100);
  14. }
  15. } キャッチ (InterruptedException e) {
  16.  
  17. }
  18. }

上記の方法は、

以下はプログラムを実行した後の結果です。ガベージ コレクション後も余分なスペースがないため、java.lang.OutOfMemoryError: Java ヒープ スペース例外が発生します。

画像-20201016211017630

ヒープ メモリ オーバーフローの根本的な原因は、使用中のオブジェクトのサイズがヒープ メモリ サイズを超えていることです。

ヒープメモリスペースの設定が小さすぎます。推定される実際のヒープ サイズに基づいて、ヒープ領域を適切に設定する必要があります。

プログラムの脆弱性により、一部の静的変数が増大し続けます。たとえば、キャッシュ データの初期化が不適切だと、キャッシュが際限なく大きくなり、最終的にはヒープ メモリのオーバーフローが発生します。この状況には、適切なテストを実施し、問題が発生した後に適切なログ分析を行う以外に、おそらく適切な解決策はありません。

スタックオーバーフロー

仮想マシン スタックは、ローカル変数テーブル、オペランド スタック、動的リンク、メソッド終了などの情報を格納するために使用されます。Java メソッドが呼び出されるたびに、仮想マシン スタック内にこのメソッドのスタック フレームが生成されます。

スタックには、仮想マシン スタックに加えて、ネイティブ メソッド スタックも含まれます。呼び出されたメソッドがネイティブ メソッド (C 言語で実装されたメソッドなど) の場合、ネイティブ メソッド スタックが使用されます。ただし、HotSpot 仮想マシンでは、仮想マシン スタックとローカル メソッド スタックが 1 つに結合されます。

スタックオーバーフローのシナリオをシミュレートする

  1. 公共 静的void main(String[] args){
  2. スタックオーバーフロー();
  3. }
  4.  
  5. /**
  6. * スタックオーバーフロー
  7. */
  8. 公共 静的voidスタックオーバーフロー() {
  9. スタックオーバーフロー();
  10. }

上記のコードでは、stackOverflow() メソッドの呼び出しは、再帰終了のない無限再帰プロセスです。前述したように、メソッドが呼び出されるたびに、仮想マシン スタックにスタック フレームが生成されます。無限再帰は必然的にスタック フレームの無限生成を引き起こし、最終的にはスタック領域がいっぱいになり、オーバーフローが発生します。

画像-20201019122447325

上記は最も一般的な状況をシミュレートしたものです。この状況の原因はおそらくプログラムのバグによるものです。一般的に言えば、再帰には再帰的な終了が必要です。何らかの理由でプログラムが実行中に終了条件に到達できない場合、この例外が発生します。ループ本体もございます。ループ本体の反復回数が多すぎると、スタック オーバーフローが発生する可能性があります。

スレッドが多すぎるなど、可能性の低い他の理由もあるかもしれません。スレッドを作成するには、仮想マシン スタックにスペースを割り当てる必要があります。作成されるスレッドが多すぎると、OutOfMemoryError 例外が発生する可能性があります。ただし、一般的には手動でスレッドを作成するのではなく、スレッド プール方式を使用するため、このような状況が発生する可能性は低くなります。

メタスペース オーバーフローは、クラス情報、定数、静的変数、ジャストインタイム (JIT) コンパイル コード、および仮想マシンによってロードされたその他のデータを格納するために使用されます。 JDK 8 では、permanent 世代の代わりに metaSpace が使用されています。デフォルトでは、metaSpace のサイズは無制限、つまりサーバーの実際のメモリ サイズになります。ただし、一般的には、メタスペースのサイズを設定するのが最適です。

一般的に、動的に生成されるクラスが大量に生成されると、メタスペースのメモリ オーバーフローが発生する可能性があります。

メタスペースオーバーフローのシミュレーション

  1. 公共 静的void main(String[] args){
  2. リスト<byte[]> byteList = 新しいArrayList<>();
  3. //静かにCrashHeapを待機します(byteList);
  4. // スタックオーバーフロー();
  5. メソッドAreaOverflow();
  6. }
  7.  
  8. 公共 静的voidメソッドAreaOverflow() {
  9. 整数i = 0;
  10. )の間{
  11. エンハンサー enhancer = new Enhancer();
  12. エンハンサー.setUseCache( false );
  13. エンハンサー.setSuperclass(MethodOverflow.class);
  14. エンハンサー.setCallback(新しいMethodInterceptor() {
  15. @オーバーライド
  16. パブリックオブジェクトインターセプト(オブジェクトo、メソッドメソッド、オブジェクト[]オブジェクト、メソッドプロキシメソッドプロキシ)はThrowableをスローします{
  17. メソッドProxy.invokeSuper(o, objects)を返します
  18. }
  19. });
  20. エンハンサーを作成します();
  21. System.out.println (++i) ;
  22. }
  23. }

CGLIB を通じて多くの動的クラスを動的に作成すると、メタスペースに格納されるクラス情報が増え、メタスペース オーバーフローが発生します。

画像-20201019163227576

たとえば、Spring や MyBatis などの技術フレームワークを使用する場合、Bean インスタンス クラスが動的に作成されます。さらに、Spring AOP は動的プロキシ クラスも生成します。

オフヒープメモリオーバーフロー

ほとんどの場合、メモリは JVM ヒープ メモリ内に割り当てられますが、まれにヒープ外部に直接メモリ領域を割り当てる必要がある場合もあります。オフヒープメモリを使用することによる利点はいくつかあります。

  • プロセス間で共有できるため、仮想マシン間のコピーが削減されます。
  • ガベージ コレクションの一時停止の改善: アプリケーションに、YGC または FullGC を頻繁にトリガーする長期間存続する大規模なオブジェクトがある場合は、これらのオブジェクトをヒープ外に配置することを検討できます。ヒープが大きすぎると、Java アプリケーションのパフォーマンスに影響します。オフヒープ メモリが使用される場合、それは仮想マシンではなくオペレーティング システムによって直接管理されます。その結果、ヒープ メモリを小さく保つことができ、ガベージ コレクションがアプリケーションに与える影響を軽減できます。
  • シナリオによっては、プログラムの I/O 操作のパフォーマンスが向上する場合があります。オンヒープメモリからオフヒープメモリにデータをコピーする手順は省略されます。

通常、オフヒープ メモリは、大量の頻繁な IO 操作が必要な場合に使用されます。たとえば、Netty と RocketMQ はオフヒープ メモリを使用してプロセスを高速化します。

したがって、システム メモリの使用量が非常に大きい場合は、スタックをチェックしても結果が出なかった場合、オフヒープ メモリの使用量をチェックして、オフヒープ メモリがオーバーフローしていないかどうかを確認できます。

要約する

事前に設定を行ってください

JVM の問題自体は比較的抽象的で直感的に発見するのが難しいため、プロジェクトがオンラインになる前に、コード ロジックをテストするだけでなく、スタック サイズ、ガベージ コレクターの種類など、アプリケーションのサイズと特性に応じて JVM パラメータを合理的に構成し、適切なパラメータを選択することも必要です。

さらに、ガベージ コレクション ログを保持し、メモリ オーバーフローが発生したときにダンプ ファイルを保存する必要があります。

プロセスを監視する

プログラムがオンラインのときは、Spring Admin などの軽量監視ツールを使用したり、大規模なプロジェクトの場合は Cat や SkyWallking などの分散リンク監視システムを使用したりして、JVM を適切に監視します。

現場での保護とその後の分析を提供する

パラメータ構成と監視プラットフォームがどれほど合理的であっても、例外は必ず発生します。これは正常です。例外がない場合にのみ問題が生じます。例外が発生した後、シーンはタイムリーに保存される必要があります。マルチインスタンス アプリケーションの場合は、例外が発生したインスタンスを一時的にオフラインにして、問題をトラブルシューティングすることができます。単一インスタンスのサービスの場合は、最新のログとダンプが保存されていることを速やかに確認する必要があります。確認後、サービスを再起動するアクションを実行します。

この記事はWeChatの公開アカウント「古代の凧」から転載したものです。下のQRコードからフォローできます。この記事を転載する場合は、Ancient Kite の公開アカウントにご連絡ください。

<<:  専門家の予測:2021年にクラウドコンピューティング分野で出現するトレンド

>>:  VMware が 3 年連続で Gartner Magic Quadrant の WAN エッジ インフラストラクチャのリーダーに選出

推薦する

安徽国際ビジネス専門学校のクラウド変革が明らかに:PC島からクラウド大陸への変革

デジタル化の波に後押しされ、安徽省合肥市にある公立全日制総合大学である安徽国際ビジネス職業学院は、大...

Google+が実名要件を廃止

ユーザーからの長年の苦情を受けて、Google はついに Google+ での実名使用の要件を解除し...

2019年のアプリプロモーションにおけるASOトレンド!

春節祭に向けた百度と今日頭条のプロモーション戦争が終わったばかりだ。 2018 年の初めに予測したと...

ウェブサイト最適化に関する FAQ 4: ウェブサイトにはなぜホームページだけが含まれているのですか?

先ほど、SEO に関する FAQ の 3 つの側面、 「新しいサイトが含まれないのはなぜか」、「 W...

juhost: 香港の格安 VPS、40% オフ、月額 2.99 ドルから、100M 帯域幅、1G メモリ/1 コア/20gSSD/1T トラフィック

Juhost は、安価な香港 VPS のプロモーションを開始しました。すべての香港 VPS が 40...

デジタルオーシャンはどうですか? [年] Digitalocean のカナダ データ センターの簡単なレビュー

Digitalocean の北米データセンターは米国に限定されていません。実はカナダにもずっと存在し...

ftechはどうですか? ftech ベトナム VPS サーバーの簡単なレビュー。IP で Netflix/spotify\steam\chatgpt などをブロック解除できます。

2011 年に設立されたベトナムのサーバー プロバイダーである ftech.vn は、ベトナムで仮想...

Be & Cheery との対談: コンテンツ マーケティングでトラフィックと売上の両方を生み出す方法

人気スターの楊洋をスポークスマンとして起用してトラフィックと知名度を高めたり、ヒット映画やテレビシリ...

クラウドデータ保護にバックアップ・アズ・ア・サービス・モデルが必要な理由

クラウド コンピューティングに関するセキュリティ上の懸念から、Backup as a Service...

Kunlun Labs、クラウド製品の主要なセキュリティ脆弱性の修正でVMwareに協力し感謝を受ける

VMware は 2 月 16 日にセキュリティ アドバイザリ VMSA-2022-0004 をリリ...

vpscheap-$15 年/512MB メモリ/20GB SSD/1000MB 無制限/シカゴ/coresite

vpscheap.net は、低価格 VPS 業界では比較的古い企業です。2010 年に設立され、非...

gigsgigscloud: 月額 7.3 ドルから、日本 cn2 gia VPS、200M 帯域幅、月額 7.3 ドルから、3 つのネットワークに直接接続

gigsgigscloud は日本に新しいデータセンターを追加しました。このデータセンターの VPS...

推奨: クアドラネット - $5.81/KVM/512m メモリ/15g SSD/1T トラフィック

クアドラネットの価格は常に法外なものでした。非常に高価です。なぜ突然クアドラネットから撤退し、民間の...