面接で必ず聞かれるJVMランタイムデータ領域について理解していますか?

面接で必ず聞かれるJVMランタイムデータ領域について理解していますか?

[[411100]]

序文

Java仮想マシンのランタイムデータ領域は面接でよく聞かれます。市場には多くの概念に対するさまざまな説明があり、多くの学生を混乱させる可能性があります。

どの記述が正しいのかわからない状況では、ソース コードと仕様が最適な参照資料となります。

面接中に、面接官が「なぜこのようなことになっているのですか?」と質問した場合、答え: 仕様がこのように書かれているから、ソースコードがこのように書かれているから。

この答えは非常に説得力があります。

したがって、この記事では、いくつかの議論の余地のある問題を説明する際に、Java 仮想マシン仕様を基礎として採用します。

文章

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

Java 仮想マシンは、プログラム実行中に使用されるいくつかのランタイム データ領域を定義します。

これらのデータ領域の一部は、Java 仮想マシンの起動時に作成され、仮想マシンの終了時に破棄されます。つまり、スレッド間で共有される領域、つまりヒープ、メソッド領域、およびランタイム定数プールです。

その他のデータ領域はスレッドごとに分割されます。これらのデータ領域はスレッドの作成時に作成され、スレッドの終了時に破棄されます。つまり、スレッド間で分離された領域、つまりプログラム カウンター、Java 仮想マシン スタック、ローカル メソッド スタックです。

1) プログラムカウンタレジスタ

Java 仮想マシンは複数のスレッドの同時実行をサポートし、各スレッドには独自のプログラム カウンターがあります。いつでも、各スレッドは 1 つのメソッドのコードのみを実行します。このメソッドは、スレッドの現在のメソッドと呼ばれます。

スレッドが Java メソッド (ネイティブではない) を実行している場合、プログラム カウンターは実行中の Java 仮想マシン バイトコード命令のアドレスを記録します。ネイティブ メソッドが実行されている場合、カウンター値は未定義になります。

2) Java仮想マシンスタック

各 Java 仮想マシン スレッドには独自のプライベート Java 仮想マシン スタックがあり、これはスレッドと同時に作成され、スタック フレームを格納するために使用されます。

Java 仮想マシン スタックは、Java メソッド実行のメモリ モデルを記述します。各メソッドは実行時にスタック フレームを作成し、ローカル変数テーブル、オペランド スタック、動的リンク、メソッド終了などの情報を格納します。

メソッドの呼び出しから実行までのプロセスは、仮想マシン スタック内のスタック フレームのプッシュとポップのプロセスに対応します。

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

ネイティブ メソッド スタックと Java 仮想マシン スタックの機能は非常に似ています。それらの唯一の違いは、Java 仮想マシン スタックは仮想マシンに Java メソッド (つまり、バイトコード) の実行を提供するのに対し、ネイティブ メソッド スタックは仮想マシンが使用するネイティブ メソッドを提供するという点です。

4) ヒープ

ヒープは、すべてのスレッドで共有されるランタイム メモリ領域であり、すべてのクラス インスタンスと配列オブジェクトにメモリが割り当てられる領域でもあります。

ヒープは仮想マシンの起動時に作成されます。ヒープに格納されたオブジェクトは明示的に解放されませんが、ガベージ コレクターによって均一に管理およびリサイクルされます。

5) 方法領域

メソッド領域は、すべてのスレッドで共有されるランタイム メモリ領域です。メソッド領域は、従来の言語のコンパイルされたコードの保存領域に似ています。実行時定数プール、フィールドとメソッドのデータ、コンストラクタと通常のメソッドのバイトコードの内容、クラス、インスタンス、およびインターフェースの初期化に使用されるいくつかの特殊メソッドなど、各クラスの構造情報を格納します。

6) 実行時定数プール

ランタイム定数プールは、クラス ファイル内の各クラスまたはインターフェイスの定数プール テーブル (constant_pool テーブル) のランタイム表現です。

これには、コンパイル時に認識される数値リテラルから、実行時に解決する必要があるメソッドおよびフィールド参照まで、さまざまな種類の定数が含まれます。ランタイム定数プールの機能は、従来のプログラミング言語のシンボル テーブルに似ていますが、そこに含まれるデータの範囲は通常の意味でのシンボル テーブルよりも広くなります。

2. Java にはどのような種類の定数プールがありますか?

現在よく言及される定数プールには、クラス ファイル定数プール、ランタイム定数プール、文字列定数プールの 3 つの主な種類があります。

3. クラスファイル定数プール

クラス ファイル定数プールは、クラス ファイルの項目の 1 つです。クラス ファイルには、マジック番号、クラス バージョン、定数プール、アクセス フラグ、フィールド テーブル セット、メソッド テーブルなどの情報が含まれています。

定数プールは、コンパイル中に生成されるさまざまなリテラルとシンボリック参照を格納するために使用されます。

リテラルは、テキスト文字列、final として宣言された定数値など、Java 言語レベルでの定数の概念に近いものです。

シンボリック参照は、コンパイル原則に関連する概念です。シンボリック参照は、参照先を記述するシンボルのセットです。シンボルは、使用時にターゲットを明確に特定できる限り、任意の形式のリテラルにすることができます (これは、メソッド領域へのローカル ポインター、相対オフセット、または間接的にターゲットを特定できるハンドルである直接参照とは区別されます)。シンボリック参照には主に次のタイプの定数が含まれます。

  • モジュールによってエクスポートまたは公開されるパッケージ
  • クラスとインターフェースの完全修飾名
  • フィールド名と記述子

定数プール内の各定数はテーブルです。 JDK 13 時点では、定数テーブルには 17 種類の定数があります。 17 種類の定数の具体的な意味を図に示します。

クラス ファイル定数プールの詳細については、Zhou Zhiming の「In-depth Understanding of Java Virtual Machine」の第 6.3.2 章を参照してください。

4. ランタイム定数プール

クラス ファイル定数プールは、クラスがクラス ファイルにコンパイルされるときに生成されます。クラスがメモリにロードされると、JVM はクラス ファイル定数プールの内容を実行時定数プールに保存します。

Java 仮想マシン仕様では、ランタイム定数プールを次のように定義しています。

ランタイム定数プールは、クラス ファイル内の constant_pool テーブルのクラスごとまたはインターフェイスごとのランタイム表現です。

ランタイム定数プールは、クラス ファイル内の各クラスまたはインターフェイスの定数プール テーブル (constant_pool テーブル) のランタイム表現です。

したがって、仕様定義によれば、ランタイム定数プールはクラスファイル定数プールのランタイム表現であり、実行時に各クラスは独自の独立したランタイム定数プールを持つと言えます。

5. 文字列定数プール

簡単に言えば、HotSpot VM の文字列定数プール (StringTable) はハッシュ テーブルであり、グローバルに 1 つのコピーのみが存在し、すべてのクラスで共有されます。

StringTable は、String オブジェクトのインスタンス自体ではなく、String オブジェクトへの参照を具体的に格納します。文字列オブジェクトのインスタンスは、JDK 6 以前では永続世代に保存され、JDK 7 以降ではヒープ内に格納されます。

Java 仮想マシン仕様の定義によれば、ヒープは Java オブジェクトが格納される場所です。他の場所には Java オブジェクト エンティティは存在しません。存在する場合、仕様の定義に従って、これらの場所もヒープの一部と見なす必要があります。

6. 文字列定数プールはメソッド領域に属しますか?

それは関係ないと思います。

この記事を読む前、多くの学生は次のような見解を持っていると思います。ランタイム定数プールはメソッド領域に属しているため、文字列定数プールもメソッド領域に属する必要があると考える学生が多いです。

しかし、上記の内容を読んだ後、ランタイム定数プールと文字列定数プールは実際には 2 つの異なるものであることに気づき始めると思います。もちろん、文字列解析中はそれらは関連しています。

Java 仮想マシン仕様では、メソッド領域を次のように定義しています。

Java 仮想マシンには、すべての Java 仮想マシン スレッド間で共有されるメソッド領域があります。メソッド領域は、従来の言語のコンパイル済みコードの保存領域、またはオペレーティング システム プロセスの「テキスト」セグメントに類似しています。実行時定数プール、フィールドとメソッドのデータ、クラスとインスタンスの初期化とインターフェースの初期化で使用される特別なメソッド(§2.9)を含むメソッドとコンストラクタのコードなどのクラスごとの構造を格納します。

Java 仮想マシンでは、メソッド領域はすべてのスレッドで共有されるランタイム メモリ領域です。メソッド領域は、従来の言語のコンパイル済みコードの保存領域、またはオペレーティング システム プロセスのテキスト セグメントに似ています。実行時定数プール、フィールドとメソッドのデータ、コンストラクタと通常のメソッドのバイトコードの内容、クラス、インスタンス、およびインターフェースの初期化に使用されるいくつかの特殊メソッドなど、各クラスの構造情報を格納します。

ここで重要なのは、「各クラスの構造情報を格納する」という点であり、文字列定数プールは特定のクラスに属していないということです。文字列定数はグローバルに共有されます。したがって、仕様定義によれば、文字列定数プールはメソッド領域に属していないと言えます。

では、文字列定数プール (StringTable) はどこにあるのでしょうか?

StringTable 自体は、永続世代でも、メソッド領域でも、もちろんヒープでもなく、ネイティブ メモリに格納されます。

7. ランタイム定数プールと文字列定数プールの関係は何ですか?

前述のように、ランタイム定数プールと文字列定数プールは、文字列解析中に次のように関連します。

クラスのランタイム定数プールには、CONSTANT_String_info 型の定数 (質問 3 の表を参照) があります。 CONSTANT_String_info 型の定数の解決プロセスは次のとおりです。

まず、文字列定数プール (StringTable) をチェックして、文字列への参照が既に存在するかどうかを確認します。そうであれば、文字列定数プールへの参照を直接返します。そうでない場合は、ヒープ内に String オブジェクトを作成し、その参照を文字列定数プールに格納してから、参照を返します。

つまり、ランタイム定数プール内の CONSTANT_String_info 型の定数は、解決された後、文字列への参照も格納し、StringTable に格納されている参照と一致します。

8. String#intern メソッド

JDK 7 以降のバージョンでは、このメソッドは次のように動作します。文字列が文字列定数プールにすでに存在する場合は、定数プール内の参照が直接返されます。そうでない場合は、文字列への参照が文字列定数プールに保存されてから返されます。

簡単な検証には次の例を使用できます。

  1. 公共 静的void main(String args[]) {
  2.  
  3. // 2つのオブジェクトを作成します。strはnewによって作成されたオブジェクト参照を保持します。
  4. // 1) 文字列定数プール内に存在するオブジェクト(内部)
  5. // 2) 新しく作成されたオブジェクト
  6. 文字列 str = 新しい文字列 ( "joonwhee" );
  7. // 文字列定数プールが既に存在する場合は、文字列定数プールへの参照を返します
  8. 文字列 str2 = "joonwhee" ;
  9. // false 、 str は new によって作成されたオブジェクト参照、 str2 は文字定数プール内の参照です
  10. システム。出力.println(str == str2);
  11. // str は文字列定数プールへの参照に変更されるため、次のようになります。  
  12. str = str.intern();
  13. //真実 
  14. システム。出力.println(str == str2);
  15. }

9. 永久世代(PermGen)

永続世代は Java 8 で削除されました。公式提案によると、削除の主な目的は JRockit と Hotspot を統合することですが、JRockit には永続世代がありません。

私たちが知る限り、もう一つの重要な理由は、永続世代自体に多くの問題があり、OOM が頻繁に発生し、多くのバグが発生していることです。

公式提案によると、永久世代には主に次の 3 種類のデータが保存されます。

1) クラス メタデータ、つまり、ネイティブ メモリに配置されるコンパイル済みバイトコードを除く、メソッド領域に含まれるデータ。

2) インターン化された文字列、つまり文字列定数プール内に常駐参照を持つ文字列オブジェクト。文字列定数プールは参照のみを保持し、実際のオブジェクトは永続的な世代にあります。

3)クラス静的変数、クラス静的変数。

永続的な世代を削除した後、インターン化された文字列とクラスの静的変数はヒープに移動され、クラスのメタデータは後のメタスペースに移動されました。

10. 永久世代とメソッド領域の関係は何ですか?

メソッド領域は、Java 仮想マシン仕様で定義された論理概念であり、永続世代はメソッド領域の実装です。ただし、永続世代はメソッド領域と同じではなく、メソッド領域は永続世代と同じではありません。

永続世代内のインターン化された文字列はメソッド領域に属しません。仕様によれば、ヒープは Java オブジェクトが格納される場所であり、この部分はヒープに属する必要があります。したがって、永続世代はメソッド領域の実装にのみ使用されるわけではありません。

メソッド領域で JIT コンパイルによって生成されたコードは、永続世代ではなくネイティブ メモリに保存されます。そのため、メソッド領域は永久世代のみで実装されているわけではないと言えます。

11. メタスペース

Metaspace は、Java 8 で permanent 世代が削除され、permanent 世代に代わるものとして導入されました。これは本質的には永続世代に似ており、メソッド area の実装です。ただし、メタスペースと永続世代の最大の違いは、メタスペースは仮想マシン内になく、ネイティブ メモリを使用することです。

メタスペースは、その名前からもわかるように、主にクラスのメタデータを格納するために使用されます。

メタスペースのサイズは、-XX:MaxMetaspaceSize パラメータによって制限できます。このパラメータが設定されていない場合、メタスペースはデフォルトでマシンのメモリに制限されます。

12. Metaspace を導入する理由は何ですか?

Java 8 より前では、Java 仮想マシンはクラス メタデータを格納するために永続世代を使用し、このメモリのサイズは -XX:PermSize と -XX:MaxPermSize によって制御されていました。動的クラスローディングが一般的になるにつれて、このメモリは制御しにくくなりました。このメモリの適切なサイズは、すべての開発者が考慮する必要がある問題です。

設定が小さすぎるとメモリオーバーフローが発生しやすくなります。設定が大きすぎると、実際にそれほど大量の物理メモリが割り当てられることはないとはいえ、少し無駄になる可能性があります。

Metaspace は、メモリ設定の大きさの問題をより適切に解決できます。-XX:MaxMetaspaceSize を指定しない場合、Metaspace はクラス数の増加に対応するために使用されるメモリ サイズを動的に調整できます。

13. Metaspace はメモリ不足の問題を完全に解決できますか?

残念ながら、答えはノーです。

Metaspace はメモリ オーバーフローの問題を完全に解決することはできませんが、軽減することはできます。メモリが使い果たされると、メタスペースでもメモリ オーバーフローが発生します。最も典型的なシナリオは、メモリ リークが発生した場合です。

この記事はWeChatの公開アカウント「Programmer Jionghui」から転載したもので、以下のQRコードからフォローできます。この記事を転載する場合は、プログラマー Jiong Hui の公式アカウントまでご連絡ください。

<<:  Springboot は Baidu のオープンソース分散 ID ジェネレータ UIDGenerator を統合します

>>:  Dockerカーネル技術の原則: 名前空間のマウント

推薦する

Kubernetes ノードをより安全にアップグレードする方法

クラスターを新しい Kubernetes バージョンにアップグレードすることに不安を感じていますか?...

多くのウェブマスターや専門家はSEOスナップショットを理解している

近年、ウェブサイトのホームページの百度スナップショット(つまり、ウェブサイトのホームページの百度スナ...

マイクロソフトの重大な脆弱性が大規模なワーム攻撃につながる可能性

[CCIDnet-IT テクノロジー ニュース] マイクロソフトは最新の情報セキュリティ速報で、リモ...

クラウドネイティブテクノロジーが5Gモバイルネットワークに与える影響

[[421463]]多くの通信サービスプロバイダー (CSP) は、ネットワーク インフラストラクチ...

ローカルフォーラムが初期段階でどのように人気を集めるかについての詳細な分析

1年前、私は暇な時間を利用して、第三級都市にローカルフォーラムを設立しました。1年以上の運営を経て、...

#Shark High Defense Server# sharktech-$99/E3-1270v2/16g メモリ/2T ハードディスク/10T トラフィック

Sharktech は、優れた構成、低価格、高コストパフォーマンスを備えたロサンゼルス高防御サーバー...

今後3~5年で、クラウドコンピューティングはエンタープライズビッグデータビジネスの発展の原動力となるだろう。

昨今、経営者は「ビッグデータ」や「クラウドサービス」といった言葉を毎日のように目にするようになりまし...

SEO トレンドに関する考察: 軽量 Web サイトがトレンドになりつつあるのでしょうか?

今年6月、蔡蔡はA5に「ウェブサイトのコンテンツは毎日更新する必要があるか?」と題する記事を掲載した...

オリジナルで高品質なソフト記事を書くには?

2013年、Baiduのアルゴリズムは頻繁に更新されましたが、編集者は、Baiduのアルゴリズムがど...

張小龍が社内スピーチで言及した「アジャイル開発」の特徴は何でしょうか?

[[211663]]プロダクトのゴッドファーザーである張小龍氏は、WeChat リーダーシップ カン...

JD Cloudがワンストップハイブリッドクラウドソリューションを開始

最近、JD Cloud は新しいハイブリッド クラウド ソリューションをリリースし、ハイブリッド ク...

フレンドリーリンクはウェブサイトにどの程度の影響を与えますか?

Naihe の友人は、フレンドリーリンクのせいで多くのキーワードを失いました。そのため、Naihe ...

企業がネットワーク マーケティングを選択する理由は何ですか? ネットワーク マーケティングの利点は何ですか?

近年、多くの企業がインターネットへの移行を進めており、ブランド認知度とビジネス量の拡大を支援するネッ...

Baidu のスナップショットが 6 月 12 日のままである理由を説明してください

今朝目覚めると、海南ウェディングネットワーク、遂城旅行ネットワーク、インターネットマーケティングリサ...

ソーシャルマーケティング信奉者の告白から生まれた思考

ソーシャル メディア マーケティングの時代を受け入れるには、まず人の力を重視する必要があります。そし...