Java Virtual Machine

Architecture

  • スレッド(Thread) 所有(しょゆう)
    • Program Counter Register
    • VM Stack
    • Native Method Stack
  • スレッド(Thread) 共有(きょうゆう)
    • Heap
    • Method Area
    • Direct Memory (Non Runtime Area Parts)

Overview

JVM Architecture

  1. ClassLoader
  2. Method Area
  3. Heap: すべてのオブジェクト、関連(かんれん) するインスタンス変数(へんすう)配列(はいれつ) はヒープに格納(かくのう) されます。このメモリは共有(きょうゆう) であり、複数(ふくすう) のスレッド(かん)共有(きょうゆう) されます。
  4. JVM Language Stacks
    1. Stack Frame : 過程(かてい) 活動(かつどう) 記録(きろく) 、コンパイラが関数(かんすう) ()() しを実装(じっそう) するためのデータ構造(こうぞう)
  5. PC Registers: 現在(げんざい) 実行中(じっこうちゅう) のJava仮想(かそう) マシン命令(めいれい) のアドレスを格納(かくのう) します。Javaでは、(かく) スレッドが独自(どくじ) のPCレジスタを() っています。
  6. Native Method Stacks: ネイティブメソッドスタックは、ネイティブライブラリに依存(いぞん) するコード命令(めいれい)保持(ほじ) します。これはJava以外(いがい)言語(げんご)() かれています。
  7. Execution Engine
  8. Native Method Interface / Java Native Interface: ネイティブメソッドインターフェースはプログラミングフレームワークです。JVM(ない)実行(じっこう) されているJavaコードがライブラリやネイティブアプリケーションによって()() されることを許可(きょか) します。C、C++などのネイティブコンパイル言語(げんご)()() すことができます。
  9. Native Method Libraries: ネイティブメソッドライブラリは、実行(じっこう) エンジンが必要(ひつよう) とするネイティブライブラリ(C、C++)の集合(しゅうごう) です。

Memory Region

  • Young
  • Old
  • Metaspace

Lifecycle

JVM 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

ClassLoader Overview

  • クラスローダー: .classファイルにアクセスするサブシステムで、ローディング、リンキング、初期化(しょきか) の3つの主要(しゅよう)機能(きのう)実行(じっこう) します。
  • Loading:
    1. BootStrap ClassLoader => ブートストラップクラスパスからクラスをロードする責任(せきにん) があります。rt.jarをロードします。 java.lang.*パッケージクラスなどのコアクラスをロードします。 このローダーには最高(さいこう)優先度(ゆうせんど)(あた) えられます。
    2. Extension ClassLoader => extフォルダ**($JAVA_HOME/lib/ext)**(ない) のクラスをロードする責任(せきにん) があります。
    3. Application ClassLoader => アプリケーションレベルのクラスパス、環境(かんきょう) 変数(へんすう)指定(してい) されたパスなどをロードする責任(せきにん) があります。
  • Linking:
    1. Verify: バイトコード検証(けんしょう) ツールが、生成(せいせい) されたバイトコードが正当(せいとう) かどうかを検証(けんしょう) します。検証(けんしょう)失敗(しっぱい) すると検証(けんしょう) エラーが発生(はっせい) します。
    2. Prepare: すべてのstatic変数(へんすう) にメモリが()() てられ、デフォルト(あたい)設定(せってい) されます。
    3. Resolve: すべてのシンボリックメモリ参照(さんしょう) がMethod Areaからの(もと)参照(さんしょう)()() えられます。
  • Initialization:
    • これはClassLoadingの最終(さいしゅう) 段階(だんかい) です。 ここで、すべてのstatic変数(へんすう)(もと)(あたい)()() てられ、staticブロックが実行(じっこう) されます。

クラスファイル形式仕様

public class DemoClass implements Serializable {
    int x  = 1;
    void test(){}
}
javap -verbose DemoClass
Warning: 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 8 0000 003d
  • Constant Pool Count: hex = decimal 0017 = 23 つまり(つぎ)定数(ていすう) が22() あり、最初(さいしょ) のインデックス(あたい) は0 2バイトは1つのクラスの定数(ていすう) プールの最大(さいだい) (すう) が65536であることを(あらわ) します

クラスローダーが必要な理由

入力(にゅうりょく) はクラスファイル(.class) 出力(しゅつりょく) は2つ

  1. クラスオブジェクト(Hello Class Object)をヒープ(heap)格納(かくのう)
  2. フィールド(めい) 記述子(きじゅつし) 、メソッド(めい) 記述子(きじゅつし) などのクラスの様々(さまざま)情報(じょうほう) をメソッドエリア(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

このステップはclassbyte stream合法(ごうほう) かどうかを確認(かくにん) する責任(せきにん) があります。 悪意(あくい) のある文字列(もじれつ) ストリームではないことをbytecode verifierというもので検査(けんさ) します この段階(だんかい)検証(けんしょう) される内容(ないよう) : クラスファイル形式(けいしき) 検証(けんしょう) メタデータ検証(けんしょう) バイトコード検証(けんしょう) シンボリック参照(さんしょう) 検証(けんしょう) 検証(けんしょう)不合格(ふごうかく)場合(ばあい)java.lang.VerifyError例外(れいがい) またはそのサブクラス例外(れいがい) がスローされます

4大検証

  • クラスファイル形式検証 最初(さいしょ)検証(けんしょう)byte stream合法(ごうほう) かどうかを検証(けんしょう) クラスファイル形式仕様
  • syntax errorの検証(けんしょう)
  1. magic number CAFE BABEで(はじ) まっているか
  2. バージョン番号(ばんごう) がこのJVMで処理(しょり) できるか
  3. …etc.

このクラスのbyte stream解析(かいせき) した(あと) 、このクラスがメソッドエリア(ない)(ただ) しく格納(かくのう) できることを(しめ) します。 メソッドエリアに格納(かくのう) された(あと)(のこ) りの3つの検証(けんしょう) はすべてメソッドエリア(ない) のデータを検証(けんしょう) し、 byte streamを操作(そうさ) しなくなります。

  • metadata verifier メタデータ検証(けんしょう)
  • semantic errorの検証(けんしょう)
  1. finalクラスが継承(けいしょう) されていない
  2. finalメソッドがオーバーライドされていない
  3. 不正(ふせい) なオーバーロードがない(signatureが(おな) じで(もど)(あたい)(こと) なるなど)
  4. このクラスが抽象(ちゅうしょう) クラスでない場合(ばあい) 、すべてのインターフェースメソッドと(おや) クラスの抽象(ちゅうしょう) メソッドを実装(じっそう) しているか
  • Byte Code Verifier バイトコード検証(けんしょう)
  • クラスのメソッド内容(ないよう)検証(けんしょう)
  1. Bytecode integrity:if conditionが(おな)関数(かんすう) (がい)行番号(ぎょうばんごう) にジャンプしない
  2. (かた) 変換(へんかん) がルールに違反(いはん) していない(例:サブオブジェクトを(おや) (がた)()() てることは許可(きょか) されるが、(ぎゃく)不可(ふか) 、または継承(けいしょう) 関係(かんけい) のない(かた) にオブジェクトを()() てることは違法(いほう)
  • シンボリック参照(さんしょう) 検証(けんしょう) 定数(ていすう) プール(ない) のシンボリック参照(さんしょう)検証(けんしょう)
  1. シンボリック参照(さんしょう) (ない)完全(かんぜん) 修飾(しゅうしょく) (めい)対応(たいおう) するクラスを() つけられるか
  2. シンボリック参照(さんしょう) (ない) のフィールド、メソッドなどが現在(げんざい) のクラスからアクセスできるか シンボリック参照(さんしょう) 検証(けんしょう)目的(もくてき) は、解析(かいせき) 段階(だんかい) (Resolution)順調(じゅんちょう)(すす) むことを確保(かくほ) することです
パラメータを(あた) えて、大部分(だいぶぶん)検証(けんしょう)無効(むこう) にし、JVMの実行(じっこう)高速化(こうそくか) できます -Xverify:none

Preparation

準備(じゅんび)

  1. static variableヒープ(heap)空間(くうかん)()() て、デフォルト(あたい)設定(せってい) します。メソッドエリアに十分(じゅうぶん)空間(くうかん) がない場合(ばあい)OutOfMemoryErrorがスローされます
  2. finalで修飾(しゅうしょく) されていないstatic変数(へんすう) は、この段階(だんかい) でデフォルト(あたい)()() てられます。(れい)public static int value = 111; この時点(じてん) でvalueは0に設定(せってい) され、初期化(しょきか) 段階(だんかい) で111に設定(せってい) されます
  3. public static final int value = 111; この場合(ばあい) 、valueはこの段階(だんかい) で111に設定(せってい) されます

Resolution

解析(かいせき)

クラスファイル(ない)シンボリック参照(さんしょう) を、メモリ(ない)目的(もくてき)位置(いち)直接(ちょくせつ) ()ポインタ変換(へんかん) するステップです

Initialize

初期化(しょきか)

Parent Delegation Model

(おや) 委譲(いじょう) モデル

ClassLoader Parent Delegation

(Note: クラスローダーの親子(おやこ) 関係(かんけい)継承(けいしょう) 関係(かんけい)実装(じっそう) されるのではなく、合成(ごうせい) (Composition)関係(かんけい)(おや) ローダーのプログラムを再利用(さいりよう) します)

Runtime Data Area

Method Area

  • JVM Method Areaは、メタデータ、定数(ていすう) ランタイムプール、メソッドのコードなどのクラス構造(こうぞう)格納(かくのう) します。

Constant Pool

  • 定数(ていすう) プール: 目的(もくてき)空間(くうかん)節約(せつやく)
  • ByteCode File(ない) のすべてのVariablesとMethodsは、Class Fileの定数(ていすう) プール(ない) にSymbolic Referenceとして保存(ほぞん) されます

Constant Pool

(かた)項目(こうもく)(かた)説明(せつめい)
CONSTANT_Utf8_info (UTF8文字列(もじれつ) )tag length bytesu1 u2 u1tag(あたい) は1 UTF8文字列(もじれつ)(なが) さ UTF8文字列(もじれつ)
CONSTANT_Integer_info (整数(せいすう) )tag bytesu1 u4tag(あたい) は3 整数(せいすう) (あたい)
CONSTANT_Float_info (浮動(ふどう) 小数点(しょうすうてん) (すう) )tag bytesu1 u4tag(あたい) は4 浮動(ふどう) 小数点(しょうすうてん) 数値(すうち)
CONSTANT_Long_info ((ちょう) 整数(せいすう) )tag bytesu1 u8tag(あたい) は5 (ちょう) 整数(せいすう) (あたい)
CONSTANT_Double_info ((ばい) 精度(せいど) 浮動(ふどう) 小数点(しょうすうてん) (すう) )tag bytesu1 u8tag(あたい) は6 (ばい) 精度(せいど) 浮動(ふどう) 小数点(しょうすうてん) 数値(すうち)
CONSTANT_Class_info (クラスまたはインターフェース シンボリック参照(さんしょう) )tag name_indexu1 u2tag(あたい) は7 名前(なまえ) がどのインデックスにあるか
CONSTANT_String_info (文字列(もじれつ) リテラル)tag string_indexu1 u2tag(あたい) は8 文字列(もじれつ) がどのインデックスにあるか
CONSTANT_Fieldref_info (フィールド シンボリック参照(さんしょう) )tag class_index name_and_type_indexu1 u2 u2tag(あたい) は9 CONSTANT_Class_infoのインデックス CONSTANT_NameAndType_infoのインデックス
CONSTANT_Methodref_info (クラスのメソッド シンボリック参照(さんしょう) )tag class_index name_and_type_indexu1 u2 u2tag(あたい) は10 CONSTANT_Class_infoのインデックス CONSTANT_NameAndType_infoのインデックス
CONSTANT_InterfaceMethodref_info (インターフェース(ない) メソッド シンボリック参照(さんしょう) )tag class_index name_and_type_indexu1 u2 u2tag(あたい) は11 CONSTANT_Class_infoのインデックス CONSTANT_NameAndType_infoのインデックス
CONSTANT_NameAndType_info (フィールドまたはメソッドの名前(なまえ)(かた) シンボリック参照(さんしょう) )tag name_index descriptor_indexu1 u2 u2tag(あたい) は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

Java言語(げんご) スタックはローカル変数(へんすう)部分的(ぶぶんてき)結果(けっか)格納(かくのう) します。(かく) スレッドは独自(どくじ) のJVMスタックを() ち、 スレッドの作成(さくせい)同時(どうじ)作成(さくせい) されます。 メソッドが()() されるたびに(あたら) しいフレームが作成(さくせい) され、 メソッド()() しプロセスが完了(かんりょう) すると削除(さくじょ) されます。

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

  • ガベージコレクタ