序文 JVM メモリ モデルを正式に学習する前に、次の点に注意してください。 JVM メモリ モデルと JAVA メモリ モデルは同じ概念ではありません。 JVM メモリ モデルは、ランタイム データ領域の構造の観点から説明される概念です。一方、JAVA メモリ モデルは、メイン メモリとスレッド プライベート メモリの観点から説明されています。次の 2 つの画像からわかるように:
JAVA メモリ モデル JVM メモリ モデル
概要 ランタイム データ領域は、いくつかの主要なモジュールに分かれています (上図を参照)。 スレッド共有領域:
スレッドプライベートエリア:
この記事では、各地域を以下の視点から分析します。
スレッドプライベートエリア プログラムカウンタ プログラム カウンターはメモリ空間の小さな部分であり、現在のスレッドによって実行されたバイトコードの行番号インジケーターとして考えることができます。バイトコード インタープリターが動作しているとき、このカウンターの値を使用して、次に実行するバイトコード命令を選択します。分岐、ループ、ジャンプ、例外処理、スレッド回復はすべてこの領域に依存します。 簡単に言えば、この領域にはメソッド領域内のメソッド バイトコードへのポインタが格納され、次の命令、つまり実行される命令コードのアドレスを格納するために使用されます。 スレッドが Java メソッドを実行している場合、このカウンターは実行中の仮想マシン バイトコード命令のアドレスを記録します。ネイティブ メソッドが実行されている場合、カウンター値は空 (未定義) になります。 命令コード行が実行されると、JVM 実行エンジンはプログラム カウンターの値を更新します。 Java 仮想マシンのマルチスレッドは、スレッドを順番に切り替えてプロセッサ実行時間を割り当てることによって実装されるため、特定の瞬間にプロセッサ (マルチコア プロセッサの場合はコア) は 1 つのスレッド内の命令のみを実行します。そのため、スレッド切り替え後に正しい実行位置に戻すためには、各スレッドが独立したプログラム カウンターを持つ必要があります。スレッド間のカウンターは互いに影響を及ぼさず、独立して保存されます。このタイプのメモリ領域を「スレッド プライベート」メモリと呼びます。 (メソッドが呼び出され、そのメソッド内で別のメソッドが呼び出されます。これは、スタックの「先入れ先出し、後入れ後出し」モデルを正式に満たします)。 メモリ不足エラー: なし 仮想マシンスタック これは Java メソッド実行のメモリ モデルを記述したもので、そのライフ サイクルはスレッドのライフ サイクルと同じです。 各メソッドは実行時にスタック フレームを作成します。各スタック フレームには、ローカル変数テーブル、オペランド スタック、動的リンク、メソッド終了などが含まれます。メソッドが呼び出され、そのメソッド内で別のメソッドが呼び出されます。これにより、スタックの「先入れ先出し、後入れ後出し」モデルが正式に満たされます。つまり、メソッドの呼び出しから実行までのプロセスは、スタック フレームをプッシュしてから仮想マシン スタックにポップするまでのプロセスに対応します。 上記は、深く理解するのが難しい、非常に機械的な概念のほんの一部です。以下では、例を使用して仮想マシン スタックのストレージ コンテンツを分析します。 まず簡単なプログラムを作成します。
上記のプログラムでは、スレッドが開始されると、仮想マシンはメインスレッド main に大きなメモリ空間を割り当て、次に main メソッドにスタック フレームを割り当てて、メソッドのローカル変数を格納します。 calculate() メソッドが実行されると、対応するメソッドのローカル変数を格納するために calculate() スタック フレームが割り当てられます。 メソッドはスタック フレームという単一のメモリ領域を割り当てることに注意することが重要です。 Java は高級言語であり、コードを通じてその実行プロセスを直接確認することは困難です。基礎となるバイトコードを解凍し、実行された命令コードを抽出することで、基礎となる実行プロセスを分析します。 CalculateMain.class ファイル ディレクトリに入り、次のコマンドを実行します。 コマンド コードを CalculateMain.txt ファイルに直接出力します。
まず、calculate() メソッドを見てみましょう。上記の手順に従って、JVM の取扱説明書を照会し、上記のプログラムの実行フローを取得します。 0. int 型定数 3 を (オペランド) スタックにプッシュします。 1. int 型の値 3 をローカル変数 1 (1 は配列の添え字) に格納します。つまり、ローカル変数テーブル内の a にメモリの一部を割り当てます (3 を格納するため)。 2. int型定数4を(オペランド)スタックにプッシュします。 3. int型の値4をローカル変数2に格納します。 4. ローカル変数 1 から int 型の値をロードします。つまり、ローカル変数テーブルの値 3 を取り出してオペランド スタックにロードします。 5. ローカル変数 2 から int 型の値を読み込みます。 6. 2つの値を追加します。 7. (オペランド スタックに数値を格納しますか?) int 値 7 をローカル変数 3 に格納します。 8. ローカル変数 3 から int 型の値を読み込みます。 9. 計算された値を返します。 上記はメソッド実行時のメモリ内のローカル変数のフロー処理です。要約すると、次のとおりです。 オペランド スタックは、操作中のデータの一時的な転送ステーションに相当します。 ローカル変数テーブル: ローカル変数の保存スペース。ワード長を単位とし、0からカウントされる配列です。int、float、reference、retrueAddress型の値は1つの項目のみを占めます。 byte、short、char 型の値は、配列に格納される前に int 値に変換されます。 long 型と double 型の値は、連続する 2 つの項目を占めます。インデックスは最初の値を指します。 ただし、仮想マシンは byte、short、char を直接サポートしますが、ローカル変数テーブルとオペランド スタックでは int 値に変換され、ヒープとメソッド領域では元の型のままであることに注意してください。 オペランド スタック: データ操作のための一時スペース。ローカル変数テーブルに似ています。唯一の違いは、インデックスではなく、プッシュとポップによってアクセスされることです。 動的リンク: 実行時に動的に生成されるメソッドの JVM 命令コードのメモリ アドレスを格納します。 オブジェクトにはオブジェクト ヘッダーがあり、その中で型ポインターはメソッド領域内のクラス メタ情報を指します。 メソッド終了: メソッドを終了して次のメソッドに入るプログラム カウンターの値を格納します。 JAVAスタック構造 例外: スレッドによって要求されたスタックの深さが仮想マシンで許可されている深さより大きい場合、StackOverflowError 例外がスローされます。仮想マシン スタックを動的に拡張できる場合 (現在の Java 仮想マシンのほとんどは動的に拡張できますが、Java 仮想マシン仕様では固定長の仮想マシン スタックも許可されています)、拡張中に十分なメモリを要求できない場合は OutOfMemoryError 例外がスローされます。 ネイティブメソッドスタック ネイティブ メソッド スタックは、実際には Java 仮想マシン スタックと非常によく似ています。唯一の違いは、Java 仮想マシン スタックは Java メソッドを提供し、ネイティブ メソッド スタックはネイティブ メソッドを提供することです。仮想マシン仕様では、ネイティブ メソッド スタック内のメソッドで使用される言語、使用法、データ構造に関する必須規定がないため、特定の仮想マシンが自由に実装できます。 StackOverflowError および OutOfMemoryError 例外もスローされる可能性があります。 スレッド共有領域 方法領域 この領域には、仮想マシンによってロードされたクラス情報 (フィールド メソッドのバイトコード、一部のメソッドのコンストラクタ)、定数、静的変数、コンパイルされたコード情報など、およびクラスのすべてのフィールドとメソッドのバイトコードが格納されます。コンストラクターなどのいくつかの特殊なメソッドだけでなく、インターフェースのコードもここで定義されます。つまり、定義されたすべてのメソッドの情報がこの領域に保存されます。静的変数 + 定数 + クラス情報 (コンストラクター/インターフェース定義) + ランタイム定数プールがすべて存在します。 不連続、固定サイズ、拡張可能、またはガベージ コレクターがない場合もあります。この領域ではガベージ コレクションが存在します。ただし、頻度は低くなります。 メソッド領域は定義と概念であり、永続的な世代またはメタスペースは実装メカニズムです。 OutOfMemoryError: はい ランタイム定数プール クラス ファイルには、クラス バージョン、フィールド、メソッド、インターフェイス、およびその他の説明情報に加えて、コンパイル中に生成されるさまざまなリテラルとシンボリック参照を格納するために使用される定数プール (定数プール テーブル) も含まれています。このコンテンツは、クラスがロードされた後にメソッド領域内のランタイム定数プールに保存されます。 OutOfMemoryError: はい JAVA ヒープ ヒープは、Java 仮想マシンによって管理されるメモリの最大の領域であり、その唯一の機能はオブジェクト インスタンスを格納することです。ほぼすべてのオブジェクト (定数プールを含む) はヒープ上にメモリを割り当てます。 インスタンスの割り当てを完了するためのメモリがヒープ内に存在せず、ヒープをそれ以上拡張できない場合は、OutOfMemoryError 例外がスローされます。 ガベージコレクターの主な管理領域。 ガベージコレクションの観点から、この領域は新しい世代と古い世代に分けられます。新世代はエデン空間とサバイバー空間に分かれています。サバイバー ペースは、サバイバー From スペースとサバイバー To スペースに分かれています。次の図に示すように: 上記のエリアのサイズ分布は次のとおりです。 若い世代: ヒープ全体の1/3 古い世代: ヒープの 2/3 エデン地区:新世代の8/10 ゾーンからの生存者: 新世代の1/10 生存者ゾーン: 新世代の1/10 メモリ割り当ての観点から言えば、複数のスレッド専用の割り当てバッファを分割することができます。 ヒープ スペースの場合、本質はオブジェクト インスタンスを格納することです。ただし、パーティショニングは、オブジェクト インスタンスの割り当てと管理を改善するためだけに行われます。ヒープ空間内のオブジェクト インスタンスの管理とリサイクルについては、次の章で説明します。 同時に、物理的には連続していないかもしれませんが、論理的には連続している必要があります。 JVM メモリ モデルの全体的な構造は次のとおりです。 オブジェクトのリサイクルプロセス 以下の写真はインターネットから取得したものです。 すべてのクラスはエデンエリアに新しく作成されます。 Eden 領域がいっぱいになると、マイナー GC がトリガーされ、他のオブジェクトから参照されなくなったオブジェクトは破棄され、残りのオブジェクトは From Survivor 領域に移動されます。マイナー GC がトリガーされるたびに、オブジェクトの世代年齢が +1 されます (世代年齢はオブジェクト ヘッダーに保存されます)。 From Survivor 領域がいっぱいになると、From Survivor 領域でマイナー GC がトリガーされ、未収集オブジェクトの世代年齢が 1 ずつ増加し続け、To Survivor 領域に移動されます。このとき、エデン内の未収集オブジェクトも「To Survivor」エリアに移動されます。 「To Survivor」エリアがいっぱいになると、「From Survivor」エリアに移動されます。 オブジェクトの世代年齢が 15 に達すると、オブジェクトは古い世代に入ります (静的変数 (オブジェクト型)、データベース接続プールなど)。古い世代もいっぱいの場合は、この時点で古い領域のメモリをクリーンアップするためにメジャー GC (フル GC) が発生します。旧領域でFull GCを実行し、オブジェクトを保存できないことが判明した場合、OOM例外OutOfMemoryErrorが生成されます。 予防
|
<<: テクノロジーの選択: Docker コンテナ エンジン
>>: ブロックチェーン分散ストレージの利点は何ですか?知っておくべき4つの大きなメリット
多くの場合、数か月間独自に外部リンクを投稿し、コンテンツを作成しているのに、Web サイトがまったく...
私は1か月以上SEOを勉強しています。私には学習と練習ができる独自のウェブサイトもあります。オリジナ...
それは「始まり」の終わりなのか、それとも「終わり」の始まりなのか?中国ゲーム業界の「Half-Lif...
百度は最近大きな変化を遂げ、国民の間にパニックを引き起こしている。まず、Baidu News の検索...
数年前、業界関係者は「IBM のサービスを選択しても解雇されることはない」と言っていました。今日の競...
2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています多くの中小...
世界はますます小さくなり、情報の伝達速度はますます速くなっています。人々がコミュニケーションのために...
まず、私の経歴についてお話しします。大した経歴ではありません。私は SEO 実践者であり、ローカル ...
翻訳者 |張峰企画 |趙雲データフロー、分析、その他のソフトウェア開発など、プロジェクトごとに課題が...
2019年4月23日、中国市場で商用運用する国際パブリッククラウドである21Vianetが運営するM...
secureservers のサーバーの割引コード 20OFF1230 を見つけました。元の価格は月...
「継続的インテグレーション」に精通している方であれば、「継続的インテグレーションの使用は必須となって...
トラブルシューティングとパフォーマンス チューニングは、データベース分野では常に専門的な問題であり、...
以前は、meanservers についてあまり知りませんでしたが、今年設立された VPS ベンダーで...
1. JDチャージプラットフォームに抜け穴があり、2億元を失ったという噂昨日22時30分、JD.co...