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 エッジ インフラストラクチャのリーダーに選出

推薦する

医療ネットワークマーケティングにおけるブランドポジショニングとチャネル構築

ブランドが王様であるこの時代において、患者を安定的に集めたいのであれば、まず病院のブランドを構築しな...

softshellweb: オランダの VPS、1Gbps の帯域幅、無制限のトラフィック、カスタム ISO、500G の高防御、月額 2.4 ドル

Softshellwebはイギリスの会社で、現在はドメイン名、仮想ホスト、VPSを主な事業としており...

KVM仮想マシンがゲートウェイにアクセスできない問題のトラブルシューティング

問題の解決は簡単そうに思えることが多いですが、トラブルシューティングのプロセスは複雑で時間がかかりま...

ガートナーは、クラウド、データセンター、エッジインフラストラクチャの将来を形作る4つのトレンドを特定しました。

経済の不確実性に満ちたこの一年、インフラストラクチャおよび運用 (I&O) チームは新しいテ...

vdscom: 香港、日本、シンガポールを含む世界15の国と地域で独立したサーバー事業を展開

vdscom は、ロシアとリトアニアに登録され、10 年の運営実績を持つホスティング会社です。現在、...

SEO: 煙と雲

SEOはキーワードがすべて私は「do」という形容詞を使うのが本当に嫌いです。検索エンジンを圧倒しよう...

beastnode-$3.5/4 コア/512m メモリ/512swap/30gSSD/2T トラフィック

BeastNode はロサンゼルスに登録された会社です。2011 年から VPS 事業に携わっており...

Digitalocean-.08 サンフランシスコ VPS レビュー、それがゴミかどうか検証

昨夜、私はサンフランシスコで DigitalOcean VPS を開設しました。これは、過去 6 か...

cloudive-2g メモリ KVM/50g ハードディスク/1T トラフィック/月額 7 ドル

Cloudive はトルコに登録された会社で、KVM ベースのサービス、40G ネットワークへのアク...

IoT におけるエッジ コンピューティングとは何ですか?

「接続された」デバイスの数の増加により、大量のデータが生成されており、モノのインターネット (IoT...

宝月中新小売フォーラム - 企業はどのように新しいマーケティングモデルに変革できるか?

月収10万元の起業の夢を実現するミニプログラム起業支援プラン要約:新しい消費時代と新しいモバイル イ...

過去からの教訓:「組み合わせ」で構築されるSEO

初めてウェブサイトを作りました。最初の記事を書いてから、もう3ヶ月近くになります。苦労や苦労も感じて...

cmivps: 香港サーバー (物理マシン)、50% 割引、年間 $863.5、2*e5-2620/32g メモリ/480gSSD/30M 帯域幅/5IPv4

cmivps は現在、香港データセンターの独立サーバーを 50% 割引で提供しています。独立サーバー...

クラウドネイティブの専門家を採用するのが難しい理由と、彼らの代わりとなる役割を見つける方法

クラウド ネイティブ テクノロジーは、企業がより迅速かつ効率的にソフトウェアを配信できるように支援で...