この記事では、Java 仮想マシンのメモリ管理について詳しく説明します。必要な方は参考にしてください。
メソッド領域
仮想マシンスタック(VMスタック)
ネイティブメソッドスタック
ヒープ
プログラムカウンター 登録
は、現在のスレッドによって実行されるバイトコードの行番号インジケーターと見なすことができる比較的小さなメモリ空間です。 (バイトコード インタプリタは、このカウンタの値を変更して、実行する必要があるバイトコード命令を選択することによって機能します)。
Java 仮想マシンのマルチスレッドは、スレッドを順番に切り替えてプロセッサの実行時間を割り当てることによって実装されるため、いかなる瞬間においても、プロセッサは 1 つのスレッドでのみ命令を実行します。したがって、スレッド切り替え後に正しい実行位置に戻るためには、各スレッド間のカウンタが互いに影響を及ぼさずに独立して格納される、独立したプログラム カウンタが必要です。このようなメモリ領域を「スレッド プライベート」と呼びます。 「記憶の。
スレッドが Java メソッドを実行している場合、このカウンタは実行中の仮想マシンのバイトコード命令のアドレスを記録します。ネイティブ メソッドの場合、カウンタ値は空です。
Java 仮想マシン スタックもスレッドプライベートであり、スレッドと同じライフサイクルを持ちます。
仮想マシン スタックは、実行のメモリ モデルを表します。各メソッドが実行されると、ローカル変数テーブル、オペランド スタック、ダイナミック リンク、メソッド出口、その他の情報を保存するためにスタック フレームが作成されます。各メソッドの呼び出しから実行完了までの処理は、仮想マシンにおけるスタックフレームのスタックへのプッシュおよびスタックからのポップアウトの処理に相当する。
スタックとは通常、仮想マシン スタック、または仮想マシン スタックのローカル変数テーブル部分を指します。
ローカル変数テーブルには、コンパイル時に既知のさまざまな基本データ型、オブジェクト参照、および returnAddress 型 (バイトコード命令のアドレスを指す) が格納されます。
long 型と double 型のデータは 2 つのローカル変数空間 (スロット) を占有しますが、他のデータ型は 1 つだけを占有します。
ローカル変数テーブルに必要なメモリ空間は、メソッドを開始するときに、このメソッドがフレーム内でどのくらいのローカル スカラー空間を割り当てる必要があるか完全に決定されます。ローカル変数テーブルは、コンパイル中に変更されません。メソッドの実行。
仮想マシンの仕様では、この領域について 2 つの例外が規定されています。スレッドによって要求されたスタックの深さが仮想マシンで許可されている深さよりも大きい場合、仮想マシンのスタックが動的に拡張できる場合は StackOverflowError 例外がスローされます。十分なメモリがない場合は、拡張を適用できません。OutOfMemoryError 例外がスローされます。
は、仮想マシン スタックと非常によく似ています。ローカル メソッド スタックは、仮想マシンによって使用されるネイティブ メソッドを提供します。ネイティブ メソッドの言語、使用法、データ構造に関する必須のルールはありません。
ほとんどのアプリケーションにとって、ヒープは仮想マシンによって管理される最大のメモリ部分です。
ヒープはすべてのスレッドによって共有されるメモリ領域であり、仮想マシンの起動時に作成されます。このメモリ領域の唯一の目的はオブジェクト インスタンスを保持することであり、ほぼすべてのオブジェクト インスタンス (すべてのオブジェクト インスタンスと配列) にメモリが割り当てられます。
Java ヒープは、ガベージ コレクターによって管理される主要な領域です。最新のコレクターは基本的にストライプ コレクション方式を使用するため、Java ヒープは新世代と旧世代に細分化することもできます。
メモリ割り当ての観点から、スレッド共有 Java ヒープは、複数のスレッドプライベート割り当てバッファ (スレッド ローカル割り当てバッファ、TLAB) に分割される場合があります。
Java ヒープは、論理的に連続している限り、物理的に不連続なメモリ空間を処理できます。実装する場合、固定サイズまたはスケーラブルとして実装できますが、主流の仮想マシンはスケーラブル (-Xms、-Xmx コントロール) として実装されます。
Javaヒープと同様に、クラス情報、定数、静的変数、ジャストインタイムコンパイラでコンパイルされたコード、その他のデータを格納するために使用されます。仮想マシンによってロードされます。 Java 仮想マシンはメソッド領域をヒープの論理部分として記述しますが、Java ヒープと区別するために非ヒープと呼ばれる別名があります。
Java ヒープのような連続メモリを必要とせず、固定サイズまたは拡張性のオプションがあることに加えて、ガベージ コレクションを実装しないことも選択できます。この領域ではガベージ コレクションが発生することはほとんどありませんが、メソッド領域に入ると永遠の世代のように永続的に存在するわけではありません。この分野のメモリのリサイクルは、主に定数プールとアンロード タイプをリサイクルすることを目的としています。
はメソッド領域の一部です。クラス ファイルには、クラスのバージョン、フィールド、メソッド、インターフェイス、その他の情報に加えて、情報が含まれています。もう 1 つの情報は定数プール テーブルです。これは、コンパイル中に生成されるさまざまなリテラルおよびシンボル参照に使用されます。この内容は、クラスのロード後にメソッド領域の実行時定数プールに保存されます。
これは動的です。クラス ファイルに事前にインストールされている定数プールの内容のみがメソッド領域の実行時定数プールに入ることができます。この機能は、開発者によってよく使用されます。 String class intern().
仮想マシンが新しい命令を受け取ると、まずこの命令のパラメーターが定数プール内のクラスのシンボル参照を見つけることができるかどうかを確認し、シンボル参照をチェックします。表現されたクラスがロード、解析、および初期化されているかどうか。そうでない場合は、対応するクラスのロード プロセスを最初に実行する必要があります。
クラスロードチェック後に新しいオブジェクトにメモリを割り当てます。オブジェクトに必要なメモリの量は、クラスがロードされた後に完全に決定されます。
メモリ割り当ての完了後、仮想マシンに割り当てる必要のあるメモリ空間はゼロに初期化されます。TLAB を使用する場合、この作業は TLAB の割り当て時に事前に実行できます。
次に、仮想マシンは、オブジェクトがどのクラスのインスタンスであるか、クラスのメタデータ情報の検索方法、オブジェクトのハッシュ コード、オブジェクトの GC 生成期間など、オブジェクトに必要な設定を行います。およびその他の情報。この情報は、オブジェクトのオブジェクト ヘッダーに配置されます。
前のステップを完了すると、仮想マシンのタスクは完了しましたが、Java プログラムの観点から見ると、オブジェクトの作成が開始されたばかりで、
メモリ内のオブジェクト メモリのレイアウトは、オブジェクト ヘッダー、インスタンス データ、アライメント パディングの 3 つの部分に分けることができます。
HotSpot 仮想マシンのオブジェクト ヘッダーは 2 つの部分で構成されます:
1. ハッシュ、GC 生成経過時間、ロック ステータス フラグ、ロックを保持しているスレッド、バイアスされたスレッド ID、バイアスされたタイムスタンプなどのオブジェクト自体のランタイム データを保存します。待って。 32 ビットおよび 64 ビットの仮想マシンのデータ長はそれぞれ 32 ビットおよび 64 ビットであり、正式には「マーク ワード」と呼ばれます。
2. オブジェクトのクラス メタデータのポインターである型ポインター。仮想マシンはこのポインターを使用して、オブジェクトがどのクラスのインスタンスであるかを判断します。オブジェクトが Java 配列の場合、仮想マシンは通常の Java オブジェクトのメタデータを通じて Java オブジェクトのサイズを決定できるため、オブジェクト ヘッダーに配列の長さを記録するために使用されるデータが必要です。配列のメタデータからはサイズを決定できません。
Java プログラム スタック上の参照データは、次の 2 つの主流の方法を通じてヒープ内のオブジェクト インスタンスにアクセスできます。
ハンドル: ハンドルとして使用されるメモリの一部が Java ヒープに割り当てられ、参照にはこのオブジェクトのハンドル アドレスが格納されます。ハンドルには、オブジェクト インスタンス データと型データの特定のプロパティが含まれます。
ダイレクト ポインター: Java ヒープ オブジェクト レイアウトでは、型データ関連情報へのアクセスを防ぐ方法を考慮する必要があります。
関連する推奨事項:
Java 仮想マシンによって管理されるメモリ ランタイム データ領域の詳細な紹介
JAVA 仮想マシン関連の知識 - JVM メモリ モデルの詳細なグラフィックとテキストの説明
以上がJava 仮想マシンのメモリ管理の詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。