1. 全体的なアーキテクチャとコンポーネント
1.クラスローダー
クラス ローダー (クラス ローダー) は、.class ファイルを JVM にロードし、対応する Java クラス オブジェクト (クラス オブジェクト) を生成する役割を果たします。Java には 3 つのクラス ローダーがあります。
- Bootstram ClassLoader: C++ で実装され、JVM の一部であるコア クラス ライブラリをロードします。
- Extension ClassLoader: Java で実装された Java 拡張クラス ライブラリ (javax.* パッケージなど) をロードし、sun.misc.Launcher の一部です。
- App ClassLoader: Java で実装されたアプリケーション クラスのロードは、ClassLoader のサブクラスです。
.2.実行エンジン
実行エンジンは、バイトコードを解釈し、それを機械語命令に変換し、プログラムを実行します。JVM の実行エンジンには 2 つのモードが含まれています。
- 解釈モード: バイトコードを 1 行ずつ解釈し、対応する機械命令を実行します。このモードの利点は、すぐに開始できることですが、実行速度は遅くなります。
- コンパイラ モード: バイトコードをローカル マシン命令にコンパイルし、コンパイルされたコードを実行します。このモードの利点は、実行速度は速いですが、起動速度が遅いことです。
JVM は、プログラムの実行中に必要に応じてインタプリタ モードとコンパイラ モードを動的に選択する混合モード (Mixed Mode) を採用しています。
3.ランタイムデータ領域
ランタイム データ領域 (ランタイム データ領域) は、JVM 内のデータ ストレージ領域であり、次のコンポーネントが含まれます。
- メソッド領域: クラス情報、定数、静的変数、その他のデータを格納します。
- ヒープ (ヒープ): Java オブジェクトと配列を格納します。
- スタック(stack): Javaメソッドのローカル変数、オペランドスタック、メソッド出口などの情報を格納します。
- PC レジスタ (プログラム カウンタ): 実行中の Java メソッドのアドレスを格納します。
- ネイティブ メソッド スタック: Java がネイティブ メソッドを呼び出すためのサポートを提供します。
4.ネイティブメソッドインターフェース
ネイティブ メソッド インターフェイス (ローカル メソッド インターフェイス) を使用すると、Java コードから C または C++ で記述されたネイティブ メソッドを呼び出すことができます。ネイティブ メソッドは、JNI (Java Native Interface) を通じて実装されます。JVM は Java パラメータを C/C++ パラメータに変換し、ネイティブ メソッドを呼び出して結果を返します。
JVM の全体的なアーキテクチャとコンポーネントは、Java プログラムを実行するための基礎です。Java プログラマにとって、JVM の原理と内部メカニズムを理解することは、より効率的で安定した Java プログラムを作成するのに役立ちます。
2. コンポーネント間の相互作用
2.1クラスローダーとメソッド領域
Java クラスが初めてロードされるとき、クラス ローダーはクラスの .class ファイルをメモリに読み取り、クラスのメンバー変数とメソッドを含む対応する Class オブジェクトをメソッド領域に作成します。JVM の実行中、クラスのバイトコード、ランタイム定数プール、フィールドおよびメソッドの情報など、すべてのクラス情報がメソッド領域に保存されます。
2.2 ヒープとスタック
Java プログラムでは、オブジェクトが作成されると、そのインスタンス データがヒープに格納され、オブジェクト参照がスタックに格納されます。各 Java メソッドは、メソッドのローカル変数、オペランド スタック、メソッド出口などの情報を格納するスタック フレーム (Stack Frame) を作成し、スタック フレームもスタックに格納されます。
Java メソッドが呼び出されると、JVM はスタック内に新しいスタック フレームを作成し、そのスタック フレームをスタックの最上位にプッシュします。メソッドの実行が完了すると、JVM はスタック フレームをポップし、メモリ領域を再利用します。
2.3実行エンジンとメソッド領域
実行エンジンは、Java バイトコードを実行し、それをコンピュータが実行できる機械命令に変換する役割を果たします。バイトコードを実行する前に、実行エンジンはメソッド領域でバイトコードや定数などの情報を検索し、それらをランタイム定数プールにロードする必要があります。
2.4ネイティブ メソッド インターフェイスとネイティブ メソッド スタック
Java プログラムが C/C++ で記述されたネイティブ メソッドを呼び出す必要がある場合、JVM は Java パラメータを C/C++ パラメータに変換し、ネイティブ メソッドを呼び出します。ネイティブ メソッドの結果は Java プログラムに返されるため、C/C++ の結果を Java の結果に変換する必要があります。
ネイティブ メソッド インターフェイスは、Java プログラムで C/C++ で記述されたネイティブ メソッドを呼び出すメカニズムを提供し、Java プログラムが基礎となるシステムと対話できるようにします。
3. クラスローダーの動作原理とクラスロードのプロセス
クラス ローダー (ClassLoader) は JVM の重要な部分であり、ディスクやネットワークなどの外部ストレージから JVM のメモリに Java クラスをロードする役割を果たします。クラスローダーは「親委任モデル」を採用しています。つまり、クラスをロードする必要がある場合、最初に親クラスローダーにクラスの検索を委託し、最終的に起動クラスローダーに委任します。親クラス ローダーのいずれもクラスを見つけることができない場合、そのクラスはクラス ローダー自体によってロードされます。
クラスロードのプロセスには通常、次の 3 つの段階が含まれます。
-
ロード: クラスのバイナリ データを検索してロードします。クラス ローダーは、まずクラスの完全修飾名を通じて対応する .class ファイルを検索し、次にバイナリ データをメモリに読み取り、対応する Class オブジェクトをメモリ内に作成します。同じクラスは JVM に 1 回だけロードされることに注意してください。
-
リンク: クラスのバイナリ データを JVM のランタイム環境にマージします。接続フェーズは 3 つのステップで構成されます。
- 検証: クラスのバイナリ データが JVM 仕様に準拠しており、セキュリティ ホールがないことを確認します。
- 準備: クラスの静的変数にメモリを割り当て、デフォルト値を設定します。
- 解決策: シンボリック参照を直接参照に置き換えます。つまり、クラス、フィールド、メソッドなどの参照をメモリ アドレスに変換します。
- 初期化(Initialization):クラスの静的変数に初期値を代入し、クラスの静的コードブロックを実行します。JVM では、クラスの初期化はスレッドセーフな操作であり、クラスが 1 回だけ初期化されることが保証されます。クラスに親クラスがある場合は、親クラスが最初に初期化されます。
JVM はクラスを使用する必要がある場合にのみクラスをロードして初期化することに注意してください。このメカニズムは「遅延ロード」と呼ばれます。同時に、JVM は動的クラス ロード メカニズムもサポートしており、プログラムの実行中に Java リフレクション メカニズムを通じて新しいクラスをロードし、JVM に追加できます。
4. バイトコード命令セットの基本構文と使用法
Java コードはコンパイラによってコンパイルされた後、クロスプラットフォームの中間コードであるバイトコード (Bytecode) に変換されます。バイトコード命令セットは、Java 仮想マシン (JVM) が認識して実行できるコード形式です。バイトコード命令セットは簡潔かつコンパクトで、基盤となるハードウェア アーキテクチャとは何の関係もないため、さまざまなプラットフォームで実行できます。
バイトコード命令セットは単一バイトの命令で構成され、各命令にはオペレーション コード (Opcode) と 1 つ以上のオペランドがあります。Java 仮想マシンは、一連のバイトコード命令を実行することによって Java プログラムの実行を完了します。
以下は、バイトコード命令セットの基本的な構文と使用法です。
4.1 ロードおよびストア命令
- ローカル変数テーブルからオペランド スタックに値をロードします: iload、dload、aload など。
- オペランド スタックからローカル変数テーブルに値を保存します: istore、dstore、astore など。
4.2 操作説明
- バイナリ演算命令: iadd、dadd、isub、dsub、imul、dmul、idiv、ddiv、irem、drem など。
- ビット操作命令: ishl、ishr、iushr、iand、ior、ixor など。
4.3 型変換命令
- 整数値を他の型 (i2d、i2l、i2f など) に変換します。
- 浮動小数点値を他の型 (d2i、d2l、d2f など) に変換します。
- 長整数値を他の型 (l2i、l2d、l2f など) に変換します。
4.4 制御コマンド
- 条件付きジャンプ命令: ifeq、ifne、iflt、ifgt、ifle、ifge など。
- 無条件ジャンプ命令: goto、goto_w など。
- 戻りコマンド: ireturn、dreturn、areturn など。
4.5 オブジェクト操作命令
- 新しいオブジェクトの作成命令: new、newarray、anearray など。
- フィールド操作命令: getfield、putfield、getstatic、putstatic など。
- メソッド呼び出し命令: invokevirtual、invokespecial、invokestatic、invokeinterface など。
一般に、バイトコード命令セットは、Java プログラムを実行するための Java 仮想マシンの基本的な構文と使用法を提供し、Java プログラムのクロスプラットフォーム動作の重要な保証の 1 つでもあります。
5. JITコンパイラとAOTコンパイラ
JIT コンパイラーと AOT コンパイラーはどちらもコードをマシンコードに変換するツールですが、その動作方法とメリットとデメリットには大きな違いがあります。
JIT コンパイラー (ジャストインタイム コンパイラー) は、プログラム実行中にリアルタイムで実行できるように、バイトコードをローカル マシン コードにコンパイルします。JIT コンパイラは、プログラムの実際の実行条件に応じてホット コードを最適化し、プログラムの実行効率を向上させることができます。JIT コンパイラーには次のような利点があります。
- ジャストインタイム コンパイルにより、プリコンパイルによる長い起動時間が回避されます。
- 動的コンパイルは、プログラムの実際の動作に応じて最適化され、プログラムの実行効率が向上します。
- Java 仮想マシンと密接に組み合わされることで、プログラムの移植性と互換性が向上します。
JIT コンパイラーの欠点は次のとおりです。
- コンパイル時間が長くなり、プログラムの応答時間に影響を与える可能性があります。
- 1 回だけ実行される一部のコードの場合、JIT コンパイラは最適化せず、パフォーマンス リソースの一部を無駄にします。
- JIT コンパイラはより多くのメモリ領域を消費します。
AOT コンパイラ (Ahead-Of-Time コンパイラ) は、プログラムを実行して実行可能ファイルを生成する前に、Java バイトコードをローカル マシン コードにコンパイルします。AOT コンパイラは、静的コンパイルによりプログラム全体を最適化し、プログラムの実行効率を向上させることができます。AOT コンパイラの利点は次のとおりです。
- コンパイル時間と起動時間の短縮。
- グローバル最適化を実行してプログラム全体を最適化し、プログラムの実行効率を向上させることができます。
- プログラムは Java 仮想マシンがなくても実行できます。
AOT コンパイラの欠点は次のとおりです。
- 動的最適化が欠如しているため、プログラムの実際の動作を最適化することは不可能です。
- 移植性と互換性の問題が発生する可能性があります。
- 多くのディスク容量を消費するため、リソースに制約のある環境に適用するのは困難です。
まとめると、JIT コンパイラと AOT コンパイラには実装方法や長所と短所が異なり、それぞれが異なるシナリオに適しています。Java 仮想マシンでは、動的な最適化と移植性の向上を実現できる JIT コンパイラが主流のコンパイラですが、組み込みシステムやモバイル アプリケーションなどの特定のシナリオには、AOT コンパイラの方が適しています。
ようこそ: http://mumuxi.chat/