【Java】JVM学習(3)

JVM の全体的なメモリ構造

本地方法栈

ローカル メソッド スタックの機能は Java 仮想マシン スタックの機能と似ており、Java 仮想マシン スタックは Java 関数の呼び出しの管理に使用され、ローカル メソッド スタックはローカル メソッドの呼び出しの管理に使用されます。ただし、ネイティブ メソッドは Java ではなく C で実装されます (Object.hashcode メソッドなど)。

ネイティブ メソッド スタックは仮想マシン スタックとよく似た領域で、そのサービス オブジェクトがネイティブ メソッドです。仮想マシン スタックとネイティブ メソッド スタックを同じ領域と考えることもできます。

仮想マシンの仕様には強制的な規定はなく、さまざまなバージョンの仮想マシンを自由に実装できますが、HotSpot はローカル メソッド スタックと仮想マシン スタックを直接 1 つに結合します。

方法区

メソッド領域(Method Area)は、スレッドが共有する実行時メモリ領域です。実行時定数プール (Runtime Constant Pool) フィールドやメソッド データ、コンストラクターや通常のメソッドのバイトコード コンテンツなど、各クラスの構造情報が格納されます。また、クラス、インスタンス、インターフェイスの初期化に使用される情報も含まれます。特別な方法

メソッド領域は、JVM によるメモリの「論理的分割」です。JDK1.7 以前では、多くの開発者はメソッド領域を呼び出していました。これは、HotSpot 仮想マシンでは、設計者が永続“永久代”世代を使用して JVM 仕様のメソッド領域を実装していたためです。 。JDK1.8以降で元空间メソッド領域を実装するために使用されます。

ここに画像の説明を挿入

メタスペース

メソッド領域もヒープ領域と同様に共有メモリ領域であるため、スレッド間で共有されます。2 つのスレッドがメソッド領域内の同じクラス情報にアクセスしようとしていて、このクラスが JVM にロードされていない場合、現時点では 1 つのスレッドのみがそのクラスをロードでき、もう 1 つのスレッドは待機する必要があります。

HotSpot 仮想マシンと Java7 バージョンでは、永続世代の静的変数とランタイム定数プールがヒープに転送され、残りは JVM の非ヒープ メモリに格納されますが、Java8 バージョンでは永続世代の変数が削除されています。メソッド領域に実装された永続世代をメタスペース(クラスメタデータ)に置き換えます。メタスペースの格納場所はローカルメモリです。

Java8 が永続生成ではなくメタスペースを使用するのはなぜですか?そうすることの利点は何ですか?

公式の説明では、
永続世代の削除は HotSpot JVM と JRockit VM を統合するための取り組みであり、JRockit には永続世代がないため、永続世代を構成する必要はありません。

多くの場合、永続世代メモリが不足するか、メモリ オーバーフローが発生し、例外 java.lang.OutOfMemoryError: PermGen がスローされます。これは、JDK1.7 バージョンでは、指定する PermGen 領域のサイズが 8M であるためで、FullGC のたびに PermGen 内のクラスのメタデータ情報が収集される可能性があるため、回復率が低く、満足のいく結果が得られないためです。 PermGen に割り当てるスペースの量を決定するのも困難です。PermSize のサイズは、JVM によってロードされるクラスの総数、定数プールのサイズ、メソッドのサイズなど、多くの要因によって決まります。

ランタイム定数プール

実行時定数プール (Runtime Constant Pool) は、各クラスまたはインターフェイスの定数プール (Constant_Pool) の実行時表現です。これには、コンパイル時に既知の数値リテラルから実行時に解析する必要があるものまで、いくつかの異なる定数が含まれます。フィールド参照のみを取得できます。

ランタイム定数プールはメソッド領域の一部です。クラス定数プールと比較したランタイム定数プールのもう 1 つの重要な特徴は、動的であることです。

ヒープ

ヒープは JVM 上の最大のメモリ領域であり、適用するほとんどすべてのオブジェクトがここに保存されます。ガベージコレクションとはよく言いますが、操作の対象はヒープです。

ヒープ領域は通常、プログラムの開始時に適用されますが、必ずしもすべてが使用されるわけではありません。ヒープは通常、スケーラブルになるように設定されます。

オブジェクトが頻繁に作成されると、ますます多くのヒープ領域が占有されるため、未使用のオブジェクトを時々リサイクルする必要があります。これをJavaではGC(ガベージコレクション)と呼びます。

そのオブジェクトが作成されるとき、それはヒープ上に割り当てられますか?それともスタック上に割り当てられますか? これは、オブジェクトのタイプと、オブジェクトが Java クラス内のどこに存在するかという 2 つのことに関係します。

Java オブジェクトは、基本データ型と通常のオブジェクトに分類できます。

Javaの場合普通对象、JVM はまずヒープ上にオブジェクトを作成し、次にその参照を他の場所で使用します。たとえば、この参照を仮想マシン スタックのローカル変数テーブルに保存します。

基本数据类型(byte、short、int、long、float、double、char)には2 つのケースがあります。
メソッドの本体でプリミティブ データ型のオブジェクトを宣言すると、そのオブジェクトはスタック上に直接割り当てられます。それ以外の場合は、ヒープ上に割り当てられます。

ダイレクトメモリ(オフヒープメモリ)

ダイレクト メモリには、オフヒープ メモリというより科学的な名前が付いています。

JVM の実行中は、データを保存するためにオペレーティング システムから大きなヒープ メモリが適用されますが、同時に仮想マシン スタック、ローカル メソッド スタック、およびスタック領域と呼ばれるプログラム カウンターが存在します。オペレーティング システムの残りのメモリはオフヒープ メモリです。

これは、仮想マシンの実行時のデータ領域の一部ではなく、Java 仮想マシンの仕様で定義されているメモリ領域でもありません。NIO が使用される場合、この領域は頻繁に使用され、directByteBuffer オブジェクトを直接参照して、 Java ヒープ内で操作されます。

このメモリ部分は Java ヒープのサイズによって制限されませんが、マシンの合計メモリによって制限されます。これは -XX:MaxDirectMemorySize で設定できます (デフォルトはヒープ メモリの最大値と同じです)。 , そのため、OOM 例外も発生します。

JVM 実行メモリの全体的なプロセス

JVM はオペレーティング システム上で起動し、メモリを適用し、最初にランタイム データ領域を初期化し、次にクラスをメソッド領域にロードし、最後にメソッドを実行します。

メソッドの実行と終了のプロセスは、仮想マシン スタック内のスタック フレームのスタッキングとポップとしてメモリに反映されます。

同時に、メソッドの実行中に作成されたオブジェクトは通常ヒープに配置され、最終的にはヒープ内のオブジェクトもガベージ コレクションによってクリーンアップする必要があります。

ヒープ領域の世代分割

ヒープは新生代老年代(Tenured) に分割され、新しい世代はさらに Eden 領域と Survivor 領域に分割され、最後に Survivor は From Survivor と To Survivor で構成されます。

ここに画像の説明を挿入

GC

JVM (Java 仮想マシン) のガベージ コレクション (GC) は、実行時に使用されなくなったオブジェクトが占有するメモリ領域を自動的に再利用するために使用される自動メモリ管理メカニズムです。ガベージ コレクターは、無効なオブジェクトまたはアクセスできないオブジェクトを識別して再利用し、それらのオブジェクトが占有するメモリを、それらのオブジェクトを必要とする他のオブジェクトに再割り当てする責任があります。

メモリ不足

JVM の実行中にメモリ オーバーフロー (メモリ不足) が発生すると、通常、ヒープ オーバーフロー、スタック オーバーフロー、メソッド領域のオーバーフロー、およびネイティブ ダイレクト メモリ オーバーフローのタイプに分類できます。

堆溢出(Heap Overflow):
ヒープは、オブジェクト インスタンスを保存するために使用される JVM 内のメモリ領域です。ヒープ オーバーフローは、作成されたオブジェクトの数がヒープの容量を超えた場合、または新しいオブジェクトにメモリ領域を割り当てることができない場合に発生します。ヒープ オーバーフローの一般的な原因はメモリ リーク (メモリ リーク) です。つまり、多数の不要なオブジェクトが継続的に参照され、ガベージ コレクターによって回復できません。

栈溢出(Stack Overflow):
スタックは、メソッド呼び出しとローカル変数を保持するために使用される JVM 内のメモリ領域です。各スレッドには、メソッド呼び出しの状態を保存するための独自のスタックがあります。再帰呼び出しまたはメソッド呼び出しのレベルが深すぎると、スタックのサイズがその容量を超え、スタック オーバーフローが発生します。スタック オーバーフローは、無限再帰、または終了条件なしで自身を呼び出し続けるメソッドが原因である可能性があります。

方法区溢出(Method Area Overflow):
メソッド領域は、クラス情報、定数、静的変数、コンパイラによって最適化されたコードなどのデータを格納するために使用される JVM 内のメモリ領域です。ロードするクラスが多すぎたり、定数プール内の定数がメソッド領域の制限を超えたりした場合、メソッド領域がオーバーフローする可能性があります。さらに、動的に生成されたクラスが多すぎると、メソッド領域のオーバーフローが発生する可能性があります。

本机直接内存溢出(Direct Memory Overflow):
ネイティブ ダイレクト メモリは、JVM によって使用される、ヒープ メモリとは別のダイレクト メモリの領域です。NIO 経由でダイレクト バッファーを使用する場合は、ネイティブ ダイレクト メモリが使用されます。ネイティブ ダイレクト メモリを再利用せずに割り当て続けると、オペレーティング システムのダイレクト メモリの制限に達した後、ネイティブ ダイレクト メモリのオーバーフロー エラーが発生します。

これらのオーバーフローの問題が発生すると、通常、対応する例外 (OutOfMemoryError、StackOverflowError など) がスローされます。

おすすめ

転載: blog.csdn.net/qq_43358469/article/details/131391031