JVM メモリ領域に関する面接の質問でまた負けたと聞きました。この記事をチェックしてください!

JVM メモリ領域に関する面接の質問でまた負けたと聞きました。この記事をチェックしてください!

基本的な質問

  • Javaメモリ領域(ランタイムデータ領域)の紹介
  • Java オブジェクトを作成するプロセス (5 つのステップ、それを書き出して、各ステップで仮想マシンが何を行うかを把握しておくことが推奨されます) • オブジェクトにアクセスして検索する 2 つの方法 (ハンドルと直接ポインタ)

拡張に関する質問

  • 文字列クラスと定数プール • 8 つの基本的なラッパークラスと定数プール

1. 概要

Java プログラマーは、仮想マシンの自動メモリ管理メカニズムにより、C/C++ プログラム開発者のように新しい操作ごとに対応する削除/解放操作を記述する必要がなくなり、メモリ リークやメモリ オーバーフローの問題が発生する可能性が低くなります。 Java プログラマーがメモリ制御権を Java 仮想マシンに委ねているからこそ、メモリ リークやメモリ オーバーフローが発生すると、仮想マシンがメモリをどのように使用しているかを理解していない場合、トラブルシューティングが非常に困難な作業になります。

[[260775]]

2. ランタイムデータ領域

Java プログラムを実行するとき、Java 仮想マシンは管理するメモリを複数の異なるデータ領域に分割します。 JDK 1.8 は以前のバージョンとは少し異なります。以下でその点について説明します。

JDK 1.8 より前:

スレッド非公開:

  • プログラムカウンタ • 仮想マシンスタック • ローカルメソッドスタック

スレッドの共有:

  • ヒープ • メソッド領域 • 直接メモリ(非ランタイムデータ領域の一部)

2.1 プログラムカウンタ

プログラム カウンターは、現在のスレッドによって実行されたバイトコードの行番号のインジケーターとして見ることができる小さなメモリです。バイトコード インタープリタが動作しているとき、このカウンタの値を変更して、次に実行するバイトコード命令を選択します。分岐、ループ、ジャンプ、例外処理、スレッド回復などの機能はすべて、このカウンターに依存して完了します。

また、スレッド切り替え後に正しい実行位置に戻すためには、各スレッドが独立したプログラム カウンターを持つ必要があります。各スレッドのカウンターは互いに影響を及ぼさず、独立して保存されます。このタイプのメモリ領域を「スレッド プライベート」メモリと呼びます。

上記の紹介から、プログラム カウンターには 2 つの主な機能があることがわかります。

•バイトコード インタープリタは、プログラム カウンタを変更することで命令を順番に読み取り、順次実行、選択、ループ、例外処理などのコード フロー制御を実装します。 • 複数のスレッドの場合、プログラム カウンターは現在のスレッドの実行場所を記録するために使用され、スレッドが戻されたときに、スレッドが最後に実行された場所を知ることができます。

注: プログラム カウンターは、OutOfMemoryError が発生しない唯一のメモリ領域です。そのライフサイクルはスレッドの作成時に作成され、スレッドの終了時に終了します。

2.2 Java仮想マシンスタック

プログラム カウンターと同様に、Java 仮想マシン スタックもスレッド専用です。そのライフサイクルはスレッドのライフサイクルと同じです。 Java メソッド実行のメモリ モデルについて説明します。各メソッド呼び出しのデータはスタックを介して渡されます。

Java メモリは、ヒープ メモリ (Heap) とスタック メモリ (Stack) に大別されます。スタックは現在、仮想マシン スタック、または仮想マシン スタックのローカル変数テーブル部分と呼ばれています。 (実際、Java 仮想マシン スタックはスタック フレームで構成されており、各スタック フレームには、ローカル変数テーブル、オペランド スタック、動的リンク、メソッド終了情報が含まれます。)

ローカル変数テーブルには、主にコンパイラに認識されるさまざまなデータ型 (boolean、byte、char、short、int、float、long、double) とオブジェクト参照 (オブジェクト自体とは異なる参照型は、オブジェクトの開始アドレスを指す参照ポインターの場合もあれば、オブジェクトを表すハンドルまたはこのオブジェクトに関連する他の場所を指す場合もあります) が格納されます。

Java 仮想マシン スタックには、StackOverFlowError と OutOfMemoryError の 2 種類の例外があります。

•StackOverFlowError: Java 仮想マシン スタックのメモリ サイズが動的な拡張を許可しない場合、スレッド要求スタックの深さが現在の Java 仮想マシン スタックの最大深さを超えると、StackOverFlowError 例外がスローされます。 •OutOfMemoryError: Java 仮想マシン スタックのメモリ サイズが動的拡張を許可していて、スレッドがスタックを要求したときにメモリが使い果たされ、動的に拡張できなくなると、OutOfMemoryError 例外がスローされます。

Java 仮想マシン スタックもスレッドプライベートです。各スレッドには独自の Java 仮想マシン スタックがあり、スレッドの作成時に作成され、スレッドが終了すると終了します。

拡張: では、メソッド/関数をどのように呼び出すのでしょうか?

Java スタックは、データ構造内のスタックに例えることができます。 Java スタックに保存される主なコンテンツはスタック フレームです。各関数呼び出しごとに、対応するスタック フレームが Java スタックにプッシュされます。各関数呼び出しの後に、スタック フレームがポップアウトされます。

Java メソッドには 2 つの戻りモードがあります。

  • return ステートメント。
  • 例外をスローします。

どちらの戻り方法でも、スタック フレームがポップされます。

2.3 ネイティブメソッドスタック

ローカル メソッド スタックが果たす役割は仮想マシン スタックの役割と非常に似ていますが、仮想マシン スタックは仮想マシンが Java メソッド (つまり、バイトコード) を実行するために機能するのに対し、ローカル メソッド スタックは仮想マシンが使用するネイティブ メソッドとして機能するという違いがあります。 HotSpot 仮想マシンでは、Java 仮想マシン スタックと組み合わせられます。

ネイティブ メソッドが実行されると、ネイティブ メソッドのスタック上にスタック フレームも作成され、ネイティブ メソッドのローカル変数テーブル、オペランド スタック、動的リンク、および終了情報が格納されます。

メソッドが実行されると、対応するスタック フレームがポップされ、メモリ領域が解放されます。 StackOverFlowError や OutOfMemoryError も発生します。

2.4 ヒープ

Java ヒープは、Java 仮想マシンによって管理されるメモリの最大の部分です。これはすべてのスレッドで共有されるメモリ領域であり、仮想マシンの起動時に作成されます。このメモリ領域の唯一の目的は、オブジェクト インスタンスを格納することです。ほぼすべてのオブジェクト インスタンスと配列にメモリがここで割り当てられます。

Java ヒープは、ガベージ コレクターによって管理される主要な領域であるため、GC ヒープ (Garbage Collected Heap) とも呼ばれます。ガベージ コレクションの観点から見ると、現在ほとんどのコレクターは世代別ガベージ コレクション アルゴリズムを使用しているため、Java ヒープも新しい世代と古い世代、より詳細には Eden スペース、From Survivor、To Survivor スペースなどに細分化できます。さらに分割する目的は、メモリをより効率的に再利用するため、またはメモリをより速く割り当てるためです。

——画像出典:https://blog.csdn.net/wangbiao007/article/details/78545189[1]

2.7 直接記憶

ダイレクト メモリは仮想マシンのランタイム データ領域の一部ではなく、仮想マシン仕様で定義されたメモリ領域でもありません。ただし、このメモリ部分も頻繁に使用されます。 OutOfMemoryError 例外が発生する可能性もあります。

JDK1.4 で新しく追加された NIO (New Input/Output) クラスは、チャネルとバッファに基づく I/O メソッドを導入します。ネイティブ関数ライブラリを直接使用してオフヒープ メモリを割り当て、Java ヒープに格納されている DirectByteBuffer オブジェクトをこのメモリへの参照として使用して操作を行うことができます。これにより、Java ヒープとネイティブ ヒープ間でのデータのやり取りが回避され、一部のシナリオでパフォーマンスが大幅に向上します。

ネイティブ ダイレクト メモリの割り当ては Java ヒープによって制限されませんが、メモリであるため、ネイティブ マシンの合計メモリ サイズとプロセッサのアドレス指定空間によって制限されます。

3. HotSpot 仮想マシン オブジェクトの秘密を探る

上記の紹介を通じて、仮想マシンのメモリ状況について大まかに理解できました。ここで、HotSpot 仮想マシンの Java ヒープにおけるオブジェクトの割り当て、レイアウト、およびアクセスのプロセス全体を詳しく見てみましょう。

3.1 オブジェクトの作成

下の図は、Java オブジェクトを作成するプロセスを示しています。黙って書き出して、各ステップが何を実行しているかを理解できるようにすることをお勧めします。

① クラスのロードチェック:仮想マシンが新しい命令に遭遇すると、まずこの命令のパラメータが定数プール内でこのクラスのシンボリック参照を見つけることができるかどうかをチェックし、このシンボリック参照によって表されるクラスがロードされ、解析され、初期化されているかどうかをチェックします。そうでない場合は、まず対応するクラスのロード プロセスを実行する必要があります。

② メモリの割り当て: クラスのロードチェックに合格すると、仮想マシンは新しいオブジェクトにメモリを割り当てます。オブジェクトに必要なメモリ サイズは、クラスがロードされた後に決定できます。オブジェクトにスペースを割り当てるタスクは、Java ヒープから特定のサイズのメモリを割り当てることと同じです。割り当て方法には、「ポインタ衝突」と「フリーリスト」の 2 つがあります。割り当て方法の選択は、Java ヒープが正規かどうかによって決定され、Java ヒープが正規かどうかは、使用するガベージ コレクターに圧縮およびデフラグ機能があるかどうかによって決定されます。

メモリ割り当ての 2 つの方法: (補足コンテンツ、習得が必要)

上記の 2 つの方法のどちらを選択するかは、Java ヒープ メモリが正常かどうかによって異なります。 Java ヒープ メモリが整頓されているかどうかは、GC コレクター アルゴリズムが「マーク スイープ」か「マーク コンパクト」(「マーク圧縮」とも呼ばれる) かによって決まります。コピー アルゴリズムのメモリも優れている点に注目してください。

メモリ割り当ての同時実行の問題 (補足コンテンツ、習得が必要)

オブジェクトを作成するときに非常に重要な問題があります。それはスレッドの安全性です。実際の開発プロセスでは、オブジェクトを作成することは非常に頻繁に行われるからです。仮想マシンとしては、スレッドが安全であることを確認する必要があります。一般的に、仮想マシンはスレッドの安全性を確保するために次の 2 つの方法を使用します。

  • CAS + 失敗再試行: CAS は楽観的ロックの実装方法です。いわゆる楽観的ロックとは、競合がないと仮定して、操作が完了するたびにロックを行わないことを意味します。競合により失敗した場合は、成功するまで再試行されます。仮想マシンは、更新操作のアトミック性を確保するために、失敗時の再試行を伴う CAS を使用します。 • TLAB: 各スレッドに対して、Eden 領域にメモリ ブロックを事前に割り当てます。 JVM がスレッド内のオブジェクトにメモリを割り当てる場合、最初に TLAB にメモリを割り当てます。オブジェクトが TLAB の残りメモリよりも大きい場合、または TLAB のメモリが不足している場合は、前述の CAS を使用してメモリを割り当てます。

③ ゼロ値に初期化:メモリ割り当てが完了したら、仮想マシンは割り当てられたメモリ空間をゼロ値に初期化する必要があります(オブジェクトヘッダーを除く)。このステップにより、オブジェクトのインスタンスのフィールドを初期値を割り当てずに Java コードで直接使用できるようになり、プログラムはこれらのフィールドのデータ型に対応するゼロ値にアクセスできるようになります。

④ オブジェクト ヘッダーの設定: ゼロ値を初期化した後、仮想マシンは、オブジェクトがどのクラスのインスタンスであるか、クラスのメタデータ情報の検索方法、オブジェクトのハッシュ、オブジェクトの GC 生成年齢などの情報など、オブジェクトに必要な設定を行う必要があります。この情報はオブジェクト ヘッダーに保存されます。さらに、バイアス ロックが有効かどうかなど、仮想マシンの現在の実行状態に応じて、オブジェクト ヘッダーが異なって設定されます。

⑤init メソッドを実行する: 上記の作業が完了すると、仮想マシンの観点からは新しいオブジェクトが作成されましたが、Java プログラムの観点からはオブジェクトの作成が始まったばかりです。メソッドはまだ実行されておらず、すべてのフィールドはまだゼロです。一般的に言えば、新しい命令を実行した後、次のステップはメソッドは、プログラマーの希望に応じてオブジェクトを初期化し、実際に使用可能なオブジェクトが完全に生成されます。

3.2 オブジェクトメモリレイアウト

Hotspot 仮想マシンでは、メモリ内のオブジェクトのレイアウトは、オブジェクト ヘッダー、インスタンス データ、およびアライメント パディングの 3 つの領域に分けられます。

ホットスポット仮想マシンのオブジェクト ヘッダーには、2 つの部分の情報が含まれています。最初の部分は、オブジェクト自身のランタイム データ (ハッシュ コード、GC 生成期間、ロック ステータス フラグなど) を格納するために使用され、もう 1 つの部分は型ポインター、つまり、オブジェクトからそのクラス メタデータへのポインターです。仮想マシンはこのポインターを使用して、オブジェクトがどのクラスのインスタンスであるかを判断します。

インスタンスデータ部分は、オブジェクトに実際に格納されている有効な情報であり、プログラムで定義されたさまざまな種類のフィールドの内容でもあります。

配置パディング部分は必須ではなく、特別な意味はありません。単なるプレースホルダーとして機能します。 Hotspot 仮想マシンの自動メモリ管理システムでは、オブジェクトの開始アドレスが 8 バイトの整数倍である必要があるためです。つまり、オブジェクトのサイズは 8 バイトの整数倍である必要があります。オブジェクト ヘッダーは、正確に 8 バイトの倍数 (1 または 2) です。したがって、オブジェクト インスタンス データが整列されていない場合は、整列パディングによって補完する必要があります。

3.3 オブジェクトアクセスの場所

オブジェクトを作成する目的はそれを使用することです。私たちの Java プログラムは、スタック上の参照データを使用して、ヒープ上の特定のオブジェクトを操作します。オブジェクトにアクセスする方法は、仮想マシンの実装によって異なります。現在主流のアクセス方法は、①ハンドルを使用する方法と、②直接ポインタを使用する方法です。

  • ハンドル: ハンドルが使用される場合、メモリの一部がハンドル プールとして Java ヒープ内に割り当てられます。参照にはオブジェクトのハンドル アドレスが格納され、ハンドルにはオブジェクト インスタンス データと型データの特定のアドレス情報が含まれます。

  • 直接ポインタ: 直接ポインタ アクセスを使用する場合、Java ヒープ オブジェクトのレイアウトでは、アクセス タイプ データの関連情報をどのように配置するかを考慮する必要があり、参照に格納される直接情報はオブジェクトのアドレスになります。

これら 2 つのオブジェクト アクセス方法にはそれぞれ利点があります。ハンドルを使用してアクセスする最大の利点は、安定したハンドル アドレスが参照に格納されることです。オブジェクトを移動する場合、ハンドル内のインスタンス データ ポインターのみが変更され、参照自体は変更する必要はありません。直接ポインタ アクセスを使用する最大の利点は速度であり、これによりポインタの位置決めにかかる時間のオーバーヘッドが節約されます。

4つの主要な補足コンテンツ

文字列クラスと定数プール

1 String オブジェクトを作成する 2 つの方法:

これら 2 つの異なる作成方法には違いがあります。最初の方法は定数プールからオブジェクトを取得することであり、2 番目の方法はヒープ メモリ領域に直接新しいオブジェクトを作成することです。

覚えておいてください: new メソッドを使用するときは常に、新しいオブジェクトを作成する必要があります。

2 String 型の定数プールは特殊です。主な使用方法は 2 つあります。

  • 二重引用符を使用して直接宣言された文字列オブジェクトは、定数プールに直接格納されます。 •String オブジェクトが二重引用符で宣言されていない場合は、String が提供する intern メソッドを使用できます。 String.intern() はネイティブ メソッドです。その機能は次のとおりです。ランタイム定数プールにこの String オブジェクトの内容に等しい文字列がすでに含まれている場合は、定数プール内の文字列への参照を返します。そうでない場合は、定数プールにこの文字列と同じ内容の文字列を作成し、定数プールに作成された文字列への参照を返します。

文字列の連結

複数の文字列を連結するとオブジェクトが再作成されるので、連結しないようにしてください。文字列を変更する必要がある場合は、StringBuilder または StringBuffer を使用できます。

このステートメントではいくつのオブジェクトが作成されますか?

2 つのオブジェクトが作成されます。

確認する:


結果:


説明する:

まず、文字列「abc」が定数プールに配置され、次に新しい文字列「abc」が Java ヒープに配置されます (文字列定数「abc」はコンパイル時に定数プールに配置されることが決定され、Java ヒープ上の「abc」は実行時の初期化フェーズ中に決定されます)。その後、Java スタックの str1 は Java ヒープ上の「abc」を指します。

8 つの基本的なパッケージング クラスと定数プールの種類

  • Java 基本型のラッパー クラスのほとんどは、Byte、Short、Integer、Long、Character、Boolean などの定数プール テクノロジを実装しています。これら 5 つのラッパー クラスは、デフォルトで [-128, 127] の値を持つ対応するタイプのキャッシュ データを作成しますが、この範囲を超えても新しいオブジェクトが作成されます。 • 2 つの浮動小数点型 (Float と Double) のラッパー クラスは、定数プール テクノロジを実装していません。

整数キャッシュのソースコード:

適用シナリオ:

  • 整数 i1=40;コンパイル時に、Java はコードを Integer i1=Integer.valueOf(40); に直接カプセル化し、定数プール内のオブジェクトを使用します。
  • 整数 i1 = 新しい整数(40);この場合、新しいオブジェクトが作成されます。


より豊富な整数比較の例:

結果:


説明する:

i4 == i5 + i6 というステートメントでは、+ 演算子は Integer オブジェクトには適用できないため、最初に i5 と i6 が自動的にアンボックス化され、値が加算されます。つまり、i4 == 40 になります。次に、Integer オブジェクトを数値と直接比較することはできないため、i4 は自動的にアンボックス化され、int 値 40 に変換されます。最後に、このステートメントは数値比較のために 40 == 40 に変換されます。

なぜいつもあなたより優れている人がいるのでしょうか?それは彼らが優秀で、より良くなるために一生懸命努力している一方で、あなたは現状に満足し、心の中で密かに幸せだからです!私をフォローして、私のプライベートメッセージ「666」に返信すると、無料のJavaアーキテクチャ学習資料(高可用性、高同時実行性、高パフォーマンスと分散、JVMパフォーマンスチューニング、Springソースコード、MyBatis、Netty、Redis、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx、その他のアーキテクチャの知識ポイントのビデオ学習資料と電子書籍資料を含む)を入手できます。時間の1分1秒を有効に活用して学習し、自分自身を向上させ、「時間がない」ことを精神的な怠惰を隠すために使用しないでください。若いうちに頑張って将来の自分に説明してあげてください!

<<:  Dynatrace がガートナーの 2019 年 APM マジック クアドラントで 9 年連続リーダーに選出

>>:  クラウド コンピューティングについてはよくご存知かもしれませんが、次の「コンピューティング」についてはご存知ですか?

推薦する

注: HostGa の「言葉にできない」公式中国語サイトの説明

最近、一部のネットユーザーから、HostGa「Undescribable」が中国語のウェブサイトを開...

VPS 中小企業、ブラックフライデー プロモーション リスト

corgitech.com 、ブラックフライデー VPS スペシャル、VMware 仮想化ベース、W...

#BlackFriday# コンタクト: 「データセンター追加料金」無料、「セットアップ料金」無料、NVMe 50% オフ、VPS、VDS、専用サーバーに適用

Contaboはブラックフライデーのプロモーション情報を公開しました:(1)米国データセンターの場合...

greencloudvps: ロサンゼルス VPS、年間 24 ドル、1G メモリ/1 コア (Ryzen)/15g NVMe/1.5T トラフィック

greencloudvps は、米国ロサンゼルス データ センターに AMD RYZEN シリーズ ...

Argo ロールアウトによるプログレッシブ リリース

Argo Rollouts は、Kubernetes Operator 実装であり、ブルーグリーン、...

適切なウェブサイト診断を行うための 12 のヒント

SEO 初心者で、企業の Web サイトを最適化する方法がわからない友人がたくさんいます。彼らはしば...

通信とエッジコンピューティングの融合がネットワークインテリジェンスをどのように再定義するか

急速に変化するテクノロジーの世界では、通信とエッジ コンピューティングの融合は、ネットワーク インテ...

企業におけるクラウドコンピューティングの習熟度向上のためのトレーニングが重要な理由

クラウド コンピューティングは、今日のテクノロジーの取得、使用、管理の方法に大きな変化をもたらしてい...

熊張浩の検索結果を印刷する際の注意点

月給5,000~50,000のこれらのプロジェクトはあなたの将来です今日は、Xiaobao Yueが...

新人ウェブマスターの告白:Baidu を取り戻す方法

Baidu を再び利用できるようにする方法は、すべてのウェブマスター、特に新しいウェブマスターにとっ...

Namecheap-17周年記念セール: ウェブホスティングは年間1ドル、.comの年間支払いは8.8ドル

Namecheap は 17 周年を迎えます。今月、Namecheap は特別プロモーションを実施し...

キーワード検索の変化から2012年インターネットビジネスカンファレンスでのジャック・マーのスピーチを検証

2012年のインターネットビジネスカンファレンスの閉会のスピーチで、ジャック・マー氏は「起業家は消費...

垂直情報プラットフォーム:次の爆発点? 垂直コミュニティについて考える

私は「卸売トラフィックの達人」というタイトルの記事を書き、Qunar、Meilishuo、Rong3...

クラウド コンピューティングにおける優れた予測のための CTO ガイド

このクラウド コンピューティング予測ガイドでは、最高技術責任者 (CTO) とクラウド コンピューテ...

SEO の段階的な発展の傾向はどこにありますか?

1. SERP機能の台頭以前は、オーガニックランキングができるだけ多くのトラフィックを獲得する方法だ...