Java 面接の必須テスト ポイント -- 講義 03: JVM を簡単に理解する

このクラスのトピックは JVM の原則です。JVM は Java プログラムを実行するための基盤であり、面接では必ず JVM 関連の質問に遭遇するでしょう。このレッスンでは、まずインタビューでの JVM 検査ポイントを要約して紹介します。次に、JVM メモリ モデル、Java のクラス ロード メカニズム、一般的に使用される GC アルゴリズムの 3 つの知識ポイントについて詳しく説明します。最後に、JVM 検査のポイントとボーナス ポイント、およびこの部分の知識に関する実際の面接の質問をまとめます。

JVM ナレッジポイントの概要

まず、JVM の知識ポイントの概要を見てみましょう。

上の図に示すように、JVM ナレッジ ポイントには 6 つの一般的な方向性があり、その中でメモリ モデル、クラス ロード メカニズム、および GC ガベージ コレクションがより重要です。パフォーマンス チューニングの部分では、実用的なアプリケーションに重点を置き、実用的な機能に重点を置いています。コンパイラの最適化と実行モードの部分では理論的基礎に焦点を当て、主に知識ポイントを習得します。

各部について知っておくべき知識は以下の通りです。

  • メモリ モデル: プログラム カウンター、メソッド領域、ヒープ、スタック、ローカル メソッド スタックの役割、およびどのようなデータが保存されるか。

  • クラスのロード: 親委任のロード メカニズム、および一般的なクラス ローダーによってロードされるクラスのタイプ。

  • GC: 世代別リサイクルのアイデアと基礎、およびさまざまなガベージ コレクション アルゴリズムを実装するためのアイデアと適切なシナリオ。

  • パフォーマンス チューニング: 一般的に使用される JVM 最適化パラメーターの役割、パラメーター チューニングの基礎、一般的に使用される JVM 分析ツールでどのような種類の問題を分析できるか、およびその使用方法。

  • 実行モード: 解釈、コンパイル、混合モードの長所と短所、および Java7 によって提供される階層化コンパイル テクノロジ。JIT ジャストインタイム コンパイル技術と OSR (スタック上置換) を理解し、C1 および C2 コンパイラが対象とするシナリオを知る必要があります。このうち、C2 はサーバー モードを対象としており、より抜本的な最適化が行われています。 。新しい技術としては、Java10 で提供される Java で実装された Graal コンパイラについて学ぶことができます。

  • コンパイルの最適化: フロントエンド コンパイラー javac のコンパイル プロセス、AST 抽象構文ツリー、コンパイル時の最適化、および実行時の最適化。コンパイルの最適化に一般的に使用される手法には、共通部分式の削除、メソッドのインライン化、エスケープ解析、スタックへの割り当て、同期の削除などが含まれます。これらを理解することによってのみ、コンパイラに適したコードを書くことができます。

     JVM 的内容相对来说比较集中,但是对知识深度的掌握要求较高。
    
JVMメモリモデルの詳細説明

JVM メモリ モデルは、次の図に示すように、主に 5 つの部分を含むランタイム データ領域を指します。

  • スタックはメソッド スタックとも呼ばれ、スレッドにとってプライベートなものであり、スレッドが各メソッドを実行すると、同時にスタック フレームが作成され、ローカル変数テーブル、操作スタック、動的メモリなどの情報を格納するために使用されます。リンク、メソッド出口など。プッシュはメソッドが呼び出されたときに実行され、ポップはメソッドが返されたときに実行されます。

  • ローカル メソッド スタックはスタックに似ており、スレッドがメソッドを実行するときに情報を保存するためにも使用されます。違いは、スタックが Java メソッドの実行に使用されるのに対し、ローカル メソッド スタックはネイティブ メソッドの実行に使用されることです。

  • プログラム カウンタは、現在のスレッドによって実行されたバイトコードの場所を保存し、各スレッドは動作中に独立したカウンタを持ちます。プログラム カウンタは Java メソッドの実行に使用されます。ネイティブ メソッドが実行されるとき、プログラム カウンタは空です。

     栈、本地方法栈、程序计数器这三个部分都是线程独占的。
    
  • ヒープは、JVM によって管理される最大のメモリ部分です。ヒープはすべてのスレッドによって共有され、オブジェクト インスタンスの保存に使用されます。ほぼすべてのオブジェクト インスタンスがここに割り当てられます。ヒープ メモリに空き領域がない場合、OOM 例外がスローされます。オブジェクトのさまざまなライフサイクルに従って、JVM はヒープ メモリを世代別に管理し、ガベージ コレクターはオブジェクトのリサイクルを管理します。

  • メソッド領域も各スレッドが共有するメモリ領域であり、非ヒープ領域とも呼ばれます。クラス情報、定数、静的変数、仮想マシンによってロードされたジャストインタイム コンパイラによってコンパイルされたコードなどのデータを保存するために使用されます。JDK 1.7 の永続生成と JDK 1.8 のメタスペースは両方とも実装です。メソッド領域。

     面试回答这个问题时,要答出两个要点:一个是各部分的功能,另一个是哪些线程共享,哪些独占。
    
JMM メモリの可視性の詳細な説明

JMM は Java メモリ モデルであり、先ほど述べた JVM メモリ モデルとは異なります。JMM の主な目的は、プログラム内の変数のアクセス ルールを定義することです。次の図に示すように、すべての共有変数は、メインメモリ。各スレッドには独自の作業メモリがあります。作業メモリに格納されるのは、メイン メモリ内の変数のコピーです。スレッドによる変数の読み取りおよび書き込みなどの操作は、独自の作業メモリで実行する必要があり、直接読み取りおよび書き込みを行うことはできません。変数をメインメモリに書き込みます。

マルチスレッドがデータを操作する場合、たとえば、スレッド A が共有変数に値を割り当てた後、スレッド B がその値を読み取ります。A が変数を変更すると、その変数は独自のワークスペース メモリ内で変更されます。B は非表示であり、変更のみが可能です。 B のワークスペースはメイン メモリに書き戻され、B はその後の操作を実行する前にメイン メモリから自身のワークスペースを読み取ります。命令の並べ替えが存在するため、この書き込みと読み取りのシーケンスが中断される可能性があります。したがって、JMM は原子性、可視性、および順序性を保証する必要があります。

JMM保証の詳しい説明

以下の図に示すように、JMM がどのように原子性、可視性、秩序性を確保するかを見てみましょう。

原子性

JMM は、long と double を除く基本データ型の読み取りおよび書き込み操作がアトミックであることを保証します。さらに、キーワード synchronized もアトミック性の保証を提供します。synchronized のアトミック性は、Java の 2 つの高度なバイトコード命令、monitorenter およびmonitorexit によって保証されます。

可視性

JMM の可視性保証の 1 つは同期されており、もう 1 つは揮発性です。揮発性の強制変数の割り当ては同期的にメイン メモリにフラッシュ バックされ、強制変数の読み取りはメイン メモリから再ロードされるため、さまざまなスレッドが常に変数の最新の値を確認できるようになります。

秩序

秩序性は、主に揮発性と一連の事前発生の原則によって保証されます。volatile のもう 1 つの機能は、命令の並べ替えを防止し、変数の読み取りと書き込みの順序性を確保することです。

前発生の原則には、次のような一連のルールが含まれます。

  • プログラム シーケンスの原則、つまり意味論的な連続性はスレッド内で保証されなければなりません。

  • ロック ルール、つまり、同じロックを再度ロックする前にロックを解除する必要があります。

  • 前発生原則、スレッドの起動、中断、終了ルールなどの推移性。

クラスロードの仕組みの詳しい説明

クラスのロードとは、コンパイルされた Class クラス ファイル内のバイトコードをメモリに読み取り、それをメソッド領域に配置し、対応する Class オブジェクトを作成することを指します。クラスのロードはロード、リンク、初期化に分かれており、リンクには検証、準備、解析の 3 つのステップが含まれます。以下に示すように。

  1. ロードは、ファイルをメモリにロードするプロセスです。このようなバイトコード ファイルを完全修飾名で検索し、バイトコード ファイルから Class オブジェクトを作成します。

  2. 検証とはクラスファイルの内容を検証することです。その目的は、クラス ファイルが現在の仮想マシン要件を満たし、仮想マシン自体のセキュリティが危険にさらされないことを確認することです。主にファイル形式検証、メタデータ検証、バイトコード検証、シンボル参照検証の4種類があります。

  3. 準備フェーズはメモリの割り当てです。クラス変数、つまりクラス内で static によって変更される変数にメモリを割り当て、初期値を設定します。ここで注意すべきは、初期値は 0 または null であり、コードに設定された特定の値ではなく、コードに設定された値は初期化フェーズで完了します。さらに、final はコンパイル中に割り当てられるため、final で変更された静的変数はここには含まれません。

  4. 解析には主にフィールド、インターフェイス、メソッドの解析が含まれます。主に、定数プール内のシンボル参照を直接参照に置き換えるプロセスです。直接参照とは、ターゲットを直接指すポインタ、相対オフセットなどです。

  5. 初期化では、主に静的ブロックの実行と静的変数の割り当てが完了します。これはクラスのロードの最終段階であり、ロードされたクラスの親クラスが初期化されていない場合は、親クラスが最初に初期化されます。

    只有对类主动使用时,才会进行初始化,初始化的触发条件包括在创建类的实例时、访问类的静态方法或者静态变量时、Class.forName() 反射类时、或者某个子类被初始化时。
    

上の図に示すように、2 つの薄緑色の部分は、クラスのロードからクラス インスタンスの作成と使用、そしてクラス オブジェクトが使用されなくなり使用できるようになるまでのクラスのライフ サイクルを表します。 GC によってアンロードおよびリサイクルされます。ここで注意すべき点は、Java 仮想マシンに付属する 3 つのクラス ローダーによってロードされたクラスは、仮想マシンのライフ サイクル全体を通じてアンロードされないということです。アンロードできるのは、ユーザー定義のクラス ローダーによってロードされたクラスのみです。

クラスローダの詳しい説明

上の図に示すように、Java に付属する 3 つのクラス ローダーは、BootStrap 起動クラス ローダー、拡張クラス ローダー、およびアプリケーション ローダー (システム ローダーとも呼ばれます) です。画像の右側にあるオレンジ色のテキストは、さまざまなローダーに対応する読み込みディレクトリを示します。起動クラスローダーは Java ホームの lib ディレクトリにクラスをロードし、拡張ローダーは ext ディレクトリにクラスをロードし、アプリケーションローダーはクラスパスで指定されたディレクトリにクラスをロードします。これに加えて、クラスローダーをカスタマイズできます。

Java のクラス ローディングでは、親委任モードが使用されます。つまり、クラス ローダーがクラスをロードするとき、最初にリクエストを独自の親クラス ローダーに実行のために委任します。親クラス ローダーにまだ親クラス ローダーがある場合は、引き続き親クラス ローダーが実行されます。上の図の青い上向き矢印で示されているように、最上位のスタートアップ クラス ローダーまで、上向きにデリゲートします。親クラス ローダーがクラスのロードを完了できれば正常に戻りますが、親クラス ローダーがロードを完了できない場合は、子ローダーが独自にロードを試みます。写真のオレンジ色の下向き矢印のようなものです。

この親委任モデルの利点は、クラスの繰り返しロードを回避できることと、Java のコア API の改ざんを防止できることです。

世代リサイクルについて詳しく解説

Javaのヒープメモリは世代ごとに管理されていますが、なぜ世代ごとに管理されているのでしょうか?世代管理は主にガベージ コレクションを容易にするために行われます。これは 2 つの事実に基づいています。1 つは、ほとんどのオブジェクトはすぐに使用されなくなること、2 つ目は、すぐには使用できなくなるものの、長期間使用できないオブジェクトがまだ存在することです。

次の図に示すように、仮想マシンは若い世代、古い世代、永続世代に分かれています。

  • 若い世代は主に新しく作成されたオブジェクトを保存するために使用され、Eden エリアと 2 つの Survivor エリアに分かれています。ほとんどのオブジェクトはエデンエリアで生成されます。エデンエリアがいっぱいになると、生き残ったオブジェクトが2つのサバイバーエリアに交互に保存され、一定回数に達したオブジェクトが旧世代に昇格します。

  • 古い世代は、若い世代から昇格され、存続期間が長いオブジェクトを保存するために使用されます。

  • 永続世代とは主にクラス情報などの内容を格納するもので、ここでいう永続世代とはオブジェクトの分割方法のことであり、1.7 の PermGen や 1.8 以降の Metaspace のことではありません。

     根据年轻代与老年代的特点,JVM 提供了不同的垃圾回收算法。垃圾回收算法按类型可以分为引用计数法、复制法和标记清除法。
    
  • 参照カウント方式はオブジェクトが参照された回数で使用されているかどうかを判断する方法ですが、循環参照の問題を解決できないという欠点があります。

  • コピー アルゴリズムには、同じサイズの 2 つのメモリ領域 (from と to) が必要です。オブジェクトの割り当ては from ブロックでのみ実行されます。リサイクル中に、残ったオブジェクトは to ブロックにコピーされ、from ブロックは空になります。その後、分業が行われます。つまり、from ブロックは to ブロックとして、to ブロックは from ブロックとして交換されます。欠点はメモリ使用量が少ないことです。

  • マーク アンド クリア アルゴリズムは、オブジェクトのマーク付けと、使用されなくなったオブジェクトのクリアの 2 つの段階に分かれています。マーク アンド クリア アルゴリズムの欠点は、メモリの断片化が発生することです。

     JVM 中提供的年轻代回收算法 Serial、ParNew、Parallel Scavenge 都是复制算法,而 CMS、G1、ZGC 都属于标记清除算法。
    
CMSアルゴリズムの詳しい説明

世代別リサイクル理論に基づいて、いくつかの代表的なガベージ コレクション アルゴリズムを詳細に紹介しますが、最初に CMS リサイクル アルゴリズムを見てみましょう。CMSはJDK1.7以前では最も主流のガベージコレクションアルゴリズムと言えます。CMS はマーク アンド クリア アルゴリズムを使用します。これには、同時収集と短い停止という利点があります。

CMS アルゴリズムを次の図に示します。

  1. 最初の段階は初期マーキングです。この段階では世界が停止します。マークされたオブジェクトは、ルート セットから最も直接到達可能なオブジェクトのみです。

  2. 第 2 段階は同時マーキングで、GC スレッドとアプリケーション スレッドが同時に実行されます。主に、到達可能なオブジェクトをマークするために使用されます。

  3. 3 番目のステージは再マーキング ステージです。このステージはワールド ステージの 2 番目の停止です。一時停止時間は同時マーキングよりもはるかに短いですが、最初のマーキングよりわずかに長くなります。主にオブジェクトを再スキャンしてマーキングします。

  4. 4 番目のステージは同時クリーニング ステージで、同時ガベージ クリーニングを実行します。

  5. 最後のフェーズは同時リセット フェーズで、次の GC のために関連するデータ構造をリセットします。

G1アルゴリズムの詳細説明

G1 は、バージョン 1.9 以降、JVM のデフォルトのガベージ コレクション アルゴリズムとなり、高い回復率を維持しながら一時停止を削減することが特徴です。

G1 アルゴリズムは、ヒープ内の若い世代と古い世代の物理的な分割をキャンセルしますが、依然として世代コレクターに属します。G1 アルゴリズムは、下の図の小さな四角で示すように、ヒープをリージョンと呼ばれるいくつかの領域に分割します。エリアの一部は若い世代向けに、一部は高齢者向けに使用され、巨大なオブジェクトを保管するために使用される特別なパーティションもあります。

CMS と同様に、G1 はすべてのオブジェクトを走査し、オブジェクト参照をマークし、オブジェクトを消去した後、領域をコピーおよび移動して、断片化された空間を統合します。

G1のリサイクルプロセスは以下の通りです。

  1. G1 の若い世代のリサイクルでは、コピー アルゴリズムを使用して並行して収集し、収集プロセスは STW になります。

  2. G1 の古い世代がリサイクルされると、若い世代もリサイクルされます。主に次の 4 つの段階に分かれています。

  3. これはまだ、ルート オブジェクトのマーキングを完了するための最初のマーキング フェーズです。このプロセスは STW です。

  4. 同時マーキング フェーズ。このフェーズはユーザー スレッドと並行して実行されます。

  5. 最後のマーキング段階で 3 色のマーキング サイクルが完了します。

  6. コピー/パージ フェーズでは、このフェーズでは、より大きなリサイクル可能な領域を持つリージョンのリサイクルを優先します。つまり、G1 という名前の由来でもあるガベージ ファーストです。

G1 は、リージョン全体ではなく、一度にリージョンの一部のみをクリーニングする増分クリーニングを使用するため、各 GC の一時停止時間が長すぎないことが保証されます。

要約すると、G1 は物理的な分割ではなく論理的な世代であり、リサイクル プロセスと一時停止段階を理解する必要があります。さらに、G1 アルゴリズムでは、JVM パラメーターを使用してリージョン サイズを 1 ~ 32MB の範囲で設定でき、予想される最大 GC 一時停止時間などを設定できることを知っておく必要があります。興味のある読者は、CMS と G1 で使用される 3 色マーキング アルゴリズムについて簡単に理解することもできます。

ZGCの詳しい説明
ZGCの特徴

ZGC は、最新の JDK1.11 バージョンで提供される効率的なガベージ コレクション アルゴリズムです。ZGC は大規模なヒープ メモリ向けに設計されており、TB レベルのヒープをサポートできます。ZGC は非常に効率的で、10 ミリ秒未満のリサイクル一時停止時間を実現できます。

ZGC はどのようにしてこれほど高速な応答を実現しているのでしょうか? これは、ZGC の以下の特性によるものです。

  1. ZGC はカラー ポインタ テクノロジを使用します。64 ビット プラットフォームでは、ポインタの利用可能なビットが 64 ビットであることがわかっています。ZGC は 4TB ヒープの最大サポートを制限しています。このようにして、アドレス指定に必要なのは 42 ビットだけであり、残りの 22 ビットはストレージに使用できます 追加情報、カラー ポインタ技術は、ポインタの追加情報ビットを使用して、ポインタ上のオブジェクトに色を付けることです。

  2. 2 番目の機能は読み取りバリアの使用です。ZGC は、STW を介して単純かつ大雑把にグローバル ロックを実行するのではなく、読み取りバリアを使用して、GC スレッドとアプリケーション スレッドがオブジェクトの状態を同時に変更する可能性があるという問題を解決します。読み取りバリアを使用しても、単一オブジェクトの処理が遅くなる可能性があるだけです。

  3. リードバリアの機能により、STW はガベージコレクションに必要ない場合が多いため、ほとんどの場合、ZGC が並行して処理されます。これが ZGC の 3 番目の特徴です。

  4. 4つ目の特徴は、G1アルゴリズムと同じRegionに基づいていますが、Regionも分割されていますが、世代は分割されていません。ZGCのRegionはG1のように固定サイズではなく、動的にRegionのサイズが決定され、動的にRegionの作成、破棄が可能です。これにより、大きなオブジェクトの割り当て管理が向上します。

  5. 5つ目の特徴は圧縮と仕上げです。CMS アルゴリズムがオブジェクトをクリーンアップしてその場でリサイクルする場合、メモリの断片化の問題が発生します。G1 と同様に、ZGC もリサイクル後にリージョン内のオブジェクトを移動およびマージするため、断片化の問題が解決されます。

    虽然 ZGC 的大部分时间是并发进行的,但是还会有短暂的停顿。来看一下 ZGC 的回收过程。
    
ZGCのリサイクルプロセス

以下の図に示すように、リサイクルには ZGC アルゴリズムが上から下に使用されます。初期状態では、ヒープ領域全体がさまざまなサイズの多数の領域 (図の緑色の四角形) に分割されています。

リサイクルを開始するとき、ZGC はまず短い STW を実行してルートをマークします。通常、ルートの総数は少ないため、このステップは非常に短くなります。

上図に示すように、オブジェクト ポインタがマークに色付けされ、読み取りバリアが結合されて単一オブジェクトの同時実行問題が解決されます。実際には、いくつかのエッジケースに対処するために、このステージの最後に非常に短い STW 一時停止がまだありますが、このステージはほとんどの場合同時に実行されるため、この一時停止は明確に示されていません。

次のステップはクリーンアップ フェーズで、使用されなくなったオブジェクトをリサイクルします。上の図に示すように、使用されなくなったオレンジ色のオブジェクトがリサイクルされます。

最後の段階は再配置であり、再配置では、GC 後に残ったオブジェクトを移動して、メモリ領域の大きなブロックを解放し、断片化の問題を解決します。

再配置には、最初に、コレクション内のルート オブジェクトを再配置するために使用される短い STW があります。一時停止時間は、ルートの数、オブジェクトの合計アクティブ セットに対する再配置セットの比率によって異なります。

最後に、同時再配置ですが、このプロセスも読み取りバリアを介してアプリケーション スレッドと同時に実行されます。

点検ポイントとボーナスポイント
検査箇所

JVM 関連のインタビュー検査ポイントの概要は次のとおりです。

  1. JVM メモリ モデルと Java メモリ モデルを深く理解します。

  2. クラスの読み込みプロセスと親の委任メカニズムを理解するため。

  3. メモリの可視性と、Java メモリ モデルの原子性、可視性、秩序性の保証メカニズムを理解する。

  4. 一般的に使用される GC アルゴリズムの特性、実行プロセス、および適用可能なシナリオを理解する必要があります。たとえば、G1 は最大のレイテンシが必要な場合に適しており、ZGC は 64 ビット システムの大規模メモリ サービスに適しています。

  5. 一般的に使用される JVM パラメータを理解し、さまざまなパラメータを調整した場合の影響と、同時ガベージ コレクションの数や偏ったロック設定など、どのようなシナリオが適用できるかを理解する必要があります。

ボーナス

面接官にもっと良い印象を与えたいなら、これらのボーナスポイントに注目してください。

  1. コンパイラの最適化について深く理解していれば、面接官に技術的な深みを追求していると感じさせることができます。たとえば、プログラミング時にスタック割り当てを合理的に利用して GC プレッシャーを軽減する方法、インライン最適化に適したコードの書き方などを知っています。

  2. 面接官は、実践的なスキルが高い学生を好みます。たとえば、私は頻繁に発生する FullGC の問題をオンラインで解決し、メモリ リークのトラブルシューティングを行ってきました。

  3. 特定のシナリオに対する JVM 最適化の実践や最適化のアイデアがある場合、予期せぬ効果が生じる可能性があります。たとえば、同時実行性が高く待ち時間が短いシナリオの場合、GC 一時停止時間を最小限に抑えるために GC パラメータを調整する方法、キュー プロセッサのスループット レートを最大化する方法など。

  4. 最新のJVM技術動向をある程度理解していれば、面接官に深い印象を残すことにもなります。たとえば、ZGC の効率的な実装原理を理解する、Graalvm の特性を理解する、などです。

実際の質問のまとめ

JVM に関する実際の面接の質問を要約すると、実際の質問の最初の部分は次のとおりです。授業後は集中して練習することができます。

問題解決のアイデアは次のとおりです。

  • 質問 1: Java のメモリ モデルについては以前にも言及しました。面接中にこの質問に答えるときは、JVM のメモリ モデルと Java のメモリ アクセス モデルのどちらを答えたいかを必ず面接官に確認してください。間違って答えないでください。

  • 質問 2 では、若い世代が昇格するときの古い世代のスペース不足、永続世代のスペース不足など、FullGC がトリガーされるシナリオを検討する必要があります。

  • 質問 3 から 6 については、すでに説明したので繰り返しません。

後半の実際の質問は次のとおりです。

問題解決のアイデアは次のとおりです。

  • 質問 7 volatile は、メイン メモリの読み取りと書き込みの同期を強制することと、命令の並べ替えを防止することの 2 つの点に焦点を当てる必要があります。

  • 質問 8 と 9 については以前に説明しました。

  • 質問 10 は、4 つのタイプの参照 (強い参照、弱い参照、ソフト参照、仮想参照) の紹介と、それらが GC でどのように処理されるかに焦点を当てています。

  • 質問 11 では、JMC のフライト レコーダー、ヒープ解析ツール MAT、スレッド解析ツール jstack、ヒープ情報を取得する jmap など、Java に付属するいくつかのツールの機能について学習できます。

このレッスンは終了しました。次のレッスンでは、Java のもう 1 つの重要な内容であるマルチスレッドについて説明します。

おすすめ

転載: blog.csdn.net/g_z_q_/article/details/129758562