Java Virtual Machine
Architecture
- スレッド
所有
- Program Counter Register
- VM Stack
- Native Method Stack
- スレッド
共有
- Heap
- Method Area
- Direct Memory (Non Runtime Area Parts)
Overview
- ClassLoader
- Method Area
- Heap: すべてのオブジェクト、関連 するインスタンス変数 、配列 はヒープに格納 されます。このメモリは共有 であり、複数 のスレッド間 で共有 されます。
- JVM Language Stacks
- Stack Frame : 過程 活動 記録 、コンパイラが関数 呼 び出 しを実装 するためのデータ構造
- PC Registers: 現在 実行中 のJava仮想 マシン命令 のアドレスを格納 します。Javaでは、各 スレッドが独自 のPCレジスタを持 っています。
- Native Method Stacks: ネイティブメソッドスタックは、ネイティブライブラリに依存 するコード命令 を保持 します。これはJava以外 の言語 で書 かれています。
- Execution Engine
- Native Method Interface / Java Native Interface: ネイティブメソッドインターフェースはプログラミングフレームワークです。JVM内 で実行 されているJavaコードがライブラリやネイティブアプリケーションによって呼 び出 されることを許可 します。C、C++などのネイティブコンパイル言語 を呼 び出 すことができます。
- Native Method Libraries: ネイティブメソッドライブラリは、実行 エンジンが必要 とするネイティブライブラリ(C、C++)の集合 です。
Memory Region
- Young
- Old
- Metaspace
Lifecycle
Code Compilation & Execution Process
Java Code
javac: JavaクラスコードをJVMバイトコードにコンパイルする役割 を担 います
flowchart TB Java-Code[.java] Java-Byte-Code[.class] Windows[JVM in Windows] Linux[JVM in Linux] Mac[JVM in Mac] Java-Code -->|Java Compiler| Java-Byte-Code --> Windows Java-Byte-Code --> Linux Java-Byte-Code --> Mac
ClassLoader
- クラスローダー: .classファイルにアクセスするサブシステムで、ローディング、リンキング、初期化 の3つの主要 な機能 を実行 します。
- Loading:
BootStrap ClassLoader=> ブートストラップクラスパスからクラスをロードする責任 があります。rt.jarをロードします。 java.lang.*パッケージクラスなどのコアクラスをロードします。 このローダーには最高 の優先度 が与 えられます。Extension ClassLoader=> extフォルダ**($JAVA_HOME/lib/ext)**内 のクラスをロードする責任 があります。Application ClassLoader=> アプリケーションレベルのクラスパス、環境 変数 で指定 されたパスなどをロードする責任 があります。
- Linking:
- Verify: バイトコード検証 ツールが、生成 されたバイトコードが正当 かどうかを検証 します。検証 に失敗 すると検証 エラーが発生 します。
- Prepare: すべてのstatic変数 にメモリが割 り当 てられ、デフォルト値 が設定 されます。
- Resolve: すべてのシンボリックメモリ参照 がMethod Areaからの元 の参照 に置 き換 えられます。
- Initialization:
- これはClassLoadingの最終 段階 です。 ここで、すべてのstatic変数 に元 の値 が割 り当 てられ、staticブロックが実行 されます。
クラスファイル形式仕様
public class DemoClass implements Serializable {
int x = 1;
void test(){}
}javap -verbose DemoClassWarning: File .\DemoClass.class does not contain class DemoClass
Classfile /D:/CodeWorkspace/Java/java-fundamental-practice/java-jvm-learning/target/classes/com/lex/practice/classbytecode/DemoClass.class
Last modified 2023~3~~11~~; size 441 bytes
SHA-256 checksum 8b5a7305cb2e6a8e35c4ab250009f242898a08f81082cebe02cbe2405792c128
Compiled from "DemoClass.java"
public class com.lex.practice.classbytecode.DemoClass implements java.io.Serializable
minor version: 0
major version: 61
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #8 // com/lex/practice/classbytecode/DemoClass
super_class: #2 // java/lang/Object
interfaces: 1, fields: 1, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Fieldref #8.#9 // com/lex/practice/classbytecode/DemoClass.x:I
#8 = Class #10 // com/lex/practice/classbytecode/DemoClass
#9 = NameAndType #11:#12 // x:I
#10 = Utf8 com/lex/practice/classbytecode/DemoClass
#11 = Utf8 x
#12 = Utf8 I
#13 = Class #14 // java/io/Serializable
#14 = Utf8 java/io/Serializable
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 LocalVariableTable
#18 = Utf8 this
#19 = Utf8 Lcom/lex/practice/classbytecode/DemoClass;
#20 = Utf8 test
#21 = Utf8 SourceFile
#22 = Utf8 DemoClass.java
{
int x;
descriptor: I
flags: (0x0000)
public com.lex.practice.classbytecode.DemoClass();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #7 // Field x:I
9: return
LineNumberTable:
line 9: 0
line 10: 4
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/lex/practice/classbytecode/DemoClass;
void test();
descriptor: ()V
flags: (0x0000)
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 11: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 this Lcom/lex/practice/classbytecode/DemoClass;
}
SourceFile: "DemoClass.java"- Magic Number
*十六進数
の英字
はABCDEFのみ
*
cafe babe - version
52 = Java SE 80000 003d - Constant Pool Count:
hex=decimal0017=23つまり次 に定数 が22個 あり、最初 のインデックス値 は0 2バイトは1つのクラスの定数 プールの最大 数 が65536であることを表 します
クラスローダーが必要な理由
入力
はクラスファイル(.class)
出力
は2つ
- クラスオブジェクト(Hello Class Object)を
ヒープ(heap)に格納 - フィールド名
記述子
、メソッド名
記述子
などのクラスの様々
な情報
をメソッドエリア
(Method Area)に格納 クラスファイル –> クラスオブジェクト+クラス情報 5つの段階 を経 る必要 があります
flowchart LR Load[ローディング] Verify[検証] Prepare[準備] Analyze[解析] Init[初期化] Load --> Verify --> Prepare --> Analyze --> Init
- 能動的
ローディング
newキーワードを使用 して新 しいオブジェクトが作成 されるstatic methodが呼 び出 されるstatic variableがアクセスされる(ただしcompile-time定数 は例外 )- このクラスのサブクラスが初期化 されたとき、このクラスがまだ初期化 されていなければ、先 にこのクラスを初期化
- コマンドラインから実行 されるとき、ユーザーが指定 したメインクラス(public static void main(String[] args)があるクラス)
- リフレクション呼 び出 し
- 受動的
ローディング
- 上記 以外 の場合 はすべて受動的
Loading
クラスローダーによって完了 します。クラスまたはインターフェースがアクセスされると、 クラスローダーはこのクラスのclasspathを検索 し、見 つかった後 、 このクラスのバイトストリーム/バイトコード(byte code)をロードし、ロード後 にクラスオブジェクト(class object)になります クラスオブジェクトはinstance of java.lang.Classであり、クラスオブジェクトはこのクラスのすべてのmeta information( class name、super class name、methodsなど)を所有 します
flowchart LR
1{{クラスオブジェクトはheapにあるか}}
2[親クラス委譲モデルで.classファイルを探す]
3{{.classファイルは見つかったか}}
e1([クラスオブジェクトを返す])
e2([クラスオブジェクトを返す&heapに格納])
e3([ClassNotFoundExceptionをスロー])
1 -->|はい| e1
1 -->|いいえ| 2
2 --> 3 -->|はい| e2
3 -->|いいえ| e3
- カスタムクラスローダー
java.lang.Classloaderを継承 するだけで独自 のクラスローダーを定義 できます。 暗号化 されたファイルや、データベースやネットワークなどの特殊 なソースからロードするために使用 できます
Verification
このステップは
classのbyte streamが合法 かどうかを確認 する責任 があります。 悪意 のある文字列 ストリームではないことをbytecode verifierというもので検査 します この段階 で検証 される内容 : クラスファイル形式 検証 メタデータ検証 バイトコード検証 シンボリック参照 検証 検証 が不合格 の場合 、java.lang.VerifyError例外 またはそのサブクラス例外 がスローされます
4大検証
- クラスファイル形式検証
最初
の検証
:
byte streamが合法 かどうかを検証 クラスファイル形式仕様 - syntax errorの検証
- magic number CAFE BABEで始 まっているか
- バージョン番号 がこのJVMで処理 できるか
- …etc.
このクラスの
byte streamを解析 した後 、このクラスがメソッドエリア内 に正 しく格納 できることを示 します。 メソッドエリアに格納 された後 、残 りの3つの検証 はすべてメソッドエリア内 のデータを検証 し、 byte streamを操作 しなくなります。
- metadata verifier メタデータ検証
- semantic errorの検証
- finalクラスが継承 されていない
- finalメソッドがオーバーライドされていない
- 不正 なオーバーロードがない(signatureが同 じで戻 り値 が異 なるなど)
- このクラスが抽象 クラスでない場合 、すべてのインターフェースメソッドと親 クラスの抽象 メソッドを実装 しているか
- Byte Code Verifier バイトコード検証
- クラスのメソッド内容 を検証
- Bytecode integrity:if conditionが同 じ関数 外 の行番号 にジャンプしない
- 型 変換 がルールに違反 していない(例:サブオブジェクトを親 型 に割 り当 てることは許可 されるが、逆 は不可 、または継承 関係 のない型 にオブジェクトを割 り当 てることは違法 )
- シンボリック参照 検証 定数 プール内 のシンボリック参照 を検証
- シンボリック参照 内 の完全 修飾 名 が対応 するクラスを見 つけられるか
- シンボリック参照
内
のフィールド、メソッドなどが現在
のクラスからアクセスできるか
シンボリック参照
検証
の目的
は、解析
段階
(Resolution)が順調 に進 むことを確保 することです
-Xverify:nonePreparation
準備
static variableにヒープ(heap)の空間 を割 り当 て、デフォルト値 を設定 します。メソッドエリアに十分 な空間 がない場合 、OutOfMemoryErrorがスローされます- finalで修飾
されていないstatic変数
は、この段階
でデフォルト値
が割
り当
てられます。例
:
public static int value = 111;この時点 でvalueは0に設定 され、初期化 段階 で111に設定 されます public static final int value = 111;この場合 、valueはこの段階 で111に設定 されます
Resolution
解析
クラスファイル内 の
シンボリック参照を、メモリ内 の目的 の位置 を直接 指 すポインタに変換 するステップです
Initialize
初期化
Parent Delegation Model
親 委譲 モデル
(Note: クラスローダーの親子
関係
は継承
関係
で実装
されるのではなく、合成
(Composition)関係
で親
ローダーのプログラムを再利用
します)
Runtime Data Area
Method Area
- JVM Method Areaは、メタデータ、定数 ランタイムプール、メソッドのコードなどのクラス構造 を格納 します。
Constant Pool
- 定数 プール: 目的 は空間 の節約
- ByteCode File内 のすべてのVariablesとMethodsは、Class Fileの定数 プール内 にSymbolic Referenceとして保存 されます
| 型 | 項目 | 型 | 説明 |
|---|---|---|---|
| CONSTANT_Utf8_info (UTF8文字列 ) | tag length bytes | u1 u2 u1 | tag値 は1 UTF8文字列 の長 さ UTF8文字列 |
| CONSTANT_Integer_info (整数 ) | tag bytes | u1 u4 | tag値 は3 整数 値 |
| CONSTANT_Float_info (浮動 小数点 数 ) | tag bytes | u1 u4 | tag値 は4 浮動 小数点 数値 |
| CONSTANT_Long_info (長 整数 ) | tag bytes | u1 u8 | tag値 は5 長 整数 値 |
| CONSTANT_Double_info (倍 精度 浮動 小数点 数 ) | tag bytes | u1 u8 | tag値 は6 倍 精度 浮動 小数点 数値 |
| CONSTANT_Class_info (クラスまたはインターフェース シンボリック参照 ) | tag name_index | u1 u2 | tag値 は7 名前 がどのインデックスにあるか |
| CONSTANT_String_info (文字列 リテラル) | tag string_index | u1 u2 | tag値 は8 文字列 がどのインデックスにあるか |
| CONSTANT_Fieldref_info (フィールド シンボリック参照 ) | tag class_index name_and_type_index | u1 u2 u2 | tag値 は9 CONSTANT_Class_infoのインデックス CONSTANT_NameAndType_infoのインデックス |
| CONSTANT_Methodref_info (クラスのメソッド シンボリック参照 ) | tag class_index name_and_type_index | u1 u2 u2 | tag値 は10 CONSTANT_Class_infoのインデックス CONSTANT_NameAndType_infoのインデックス |
| CONSTANT_InterfaceMethodref_info (インターフェース内 メソッド シンボリック参照 ) | tag class_index name_and_type_index | u1 u2 u2 | tag値 は11 CONSTANT_Class_infoのインデックス CONSTANT_NameAndType_infoのインデックス |
| CONSTANT_NameAndType_info (フィールドまたはメソッドの名前 と型 シンボリック参照 ) | tag name_index descriptor_index | u1 u2 u2 | tag値 は12 名前 文字列 へのポインタ 型 文字列 へのポインタ |
0A = 10, tag 10 = Methodref, u1 = 1バイト u2 = 2バイト- Access Flags アクセスフラグ
- Class Name クラス名
- Super Class Name 親 クラス名
- Interfaces Count インターフェースの数
- Interfaces インターフェース
- Fields Count フィールドの数
- Fields フィールド
- Methods Count メソッドの数
- Methods メソッド
- Attributes Count 属性 の数
- Attributes 属性
JVM Language Stacks
Execution Engine
Interpreter
- インタプリタ
1行 実行 するたびに1行 コンパイルし、各
Javaバイトコードを順番 に変換 ・実行 するため、パフォーマンスは劣 ります。 一般的 に、実行 エンジンは最初 にInterpreterを使用 してJavaバイトコードを実行 し、特定 の条件 が満 たされた後 にJIT最適化 を使用 します
JIT
Just-in-time compiler即時 コンパイラ- C++で開発
- バイトコードをマシンコードに変換 する役割
バイトコードの実行 を開始 するとき、最初 はインタプリタ(Interpreter)で実行 し、各 メソッドにはカウンターcounterがあります。Method Aを1回 実行 するたびにMethod Aのcounterを+1し、メソッドの呼 び出 し回数 が特定 の閾値 を超 えると、compilerを呼 び出 してコンパイルされたマシンコードをキャッシュ(Cache)します。その後Method Aを呼 び出 すときはマシンコードで実行 し、 counterを再 び+1し、第 2の閾値 を超 えると、再 びcompilerを呼 び出 して最適化 を追加 し、 生成 されたマシンコードで以前 のものを上書 きします。これを繰 り返 し、ある閾値 以降 最適化 できなくなったら、カウントを停止 します。 この方法 は動的 コンパイルとも呼 ばれ、Interpreterの速度 を大幅 に向上 させます。 JavaScript、Python、RubyもすべてJITを使用 してインタプリタを高速化 しています。
Garbage Collector
- ガベージコレクタ