Dev Study
← 解説「JVM の起動とクラスロード、JIT」に戻る

サンプルコードで身につける: JVM の起動とクラスロード、JIT

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1クラスを読み込んだのは誰か

を読み込む係をと呼びます。String のような は 内蔵のローダが読むため、getClassLoader() は null を返します。自分で書いた Main は、アプリケーション用のローダ(AppClassLoader)が読み込んでいます。

Java

2クラスロードのログ

読み込まれたクラスを1行ずつ出すオプションを付けて Hello を実行したものです。全 609 行のうち、自分の Hello は 401 行目に file:/w/ から読み込まれています。それ以外の行のほとんどは shared objects file()が出どころです。

$ java -Xlog:class+load Hello(JDK 25、609 行から抜粋)追跡ログ
[0.006s][info][class,load] java.lang.Object source: shared objects file
[0.007s][info][class,load] java.io.Serializable source: shared objects file
[0.007s][info][class,load] java.lang.Comparable source: shared objects file
[0.007s][info][class,load] java.lang.CharSequence source: shared objects file
...
[0.020s][info][class,load] java.security.Permissions source: shared objects file
[0.020s][info][class,load] Hello source: file:/w/
[0.020s][info][class,load] java.lang.NamedPackage source: shared objects file
...
[0.026s][info][class,load] java.lang.Shutdown source: shared objects file
[0.026s][info][class,load] java.lang.Shutdown$Lock source: shared objects file

3共有アーカイブがあるときと無いとき

既定では のクラスの大半を、あらかじめ用意された共有アーカイブ(CDS)から取り込んでいました。 で切ると 620 個を jrt:/java.base から読み込むようになり、起動は各巡の1回目を除いて 14〜17 ミリ秒から 30〜34 ミリ秒に伸びました。行数は JDK の版でも変わります。

java -Xlog:class+load Hello の行数と出どころ(既定は各2回で同じ値、-Xshare:off は1回)表
条件 行数 共有アーカイブ jrt:/java.base
JDK 25 既定 609 605 2
JDK 25 -Xshare:off 622 0 620
JDK 22 既定 531 522 2

4JIT があるときと無いとき

同じを 2000 ラウンド呼び、1ラウンドの時間をマイクロ秒で出した実測です。既定では1ラウンド目が 1176 で4ラウンド目以降は 70 前後に落ち着き、(だけ)では最後まで約 600 のままでした。3ラウンド目が跳ねた理由は特定できておらず、数値はこの計算とこの機械でのものです。

$ java Warmup と java -Xint Warmup(JDK 25、各1回分)コマンド出力
## default run 1
round us: [1]=1176 [2]=223 [3]=1126 [4]=70 [5]=70 [6]=70 [7]=70 [8]=70 [9]=70 [10]=70 [20]=70 [50]=70 [100]=70 [200]=70 [500]=77 [1000]=69 [2000]=69
rounds 1001-2000 (us): min=69 median=69 max=109
## -Xint run 1
round us: [1]=674 [2]=637 [3]=620 [4]=615 [5]=597 [6]=590 [7]=590 [8]=611 [9]=597 [10]=573 [20]=597 [50]=598 [100]=600 [200]=600 [500]=611 [1000]=598 [2000]=610
rounds 1001-2000 (us): min=539 median=609 max=844

5Hello の起動時間を Node・Rust と並べる

プロセスを起こしてから終わるまでを10回ずつ2巡測った値です。各巡の1回目を除くと が 14〜17、node が 10〜12、Rust が 0.7〜2.0 ミリ秒(外れ値 6.3 が1回)で、何もしない /bin/true でも 0.4〜1.1(外れ値 3.7 が1回)かかるため、Rust の値はプロセスを起こす費用とほぼ区別できません。別々ので測っており、言えるのは順序と桁までです。

起動から終了までの時間(ms、arm64 の Docker 上、各10回×2巡)表
java Hello(JDK 25 既定)
1巡目: 21.5 15.6 16.8 15.9 16.0 16.2 15.0 15.4 17.2 17.4
2巡目: 18.0 15.7 13.9 15.3 14.5 15.9 15.7 16.0 14.9 14.6
node hello.js(v24.21.0)
1巡目: 15.0 12.3 11.3 11.5 10.8 11.0 11.2 11.1 10.7 10.9
2巡目: 17.0 11.8 10.6 9.9 9.6 10.6 11.8 10.8 11.8 10.8
Rust の実行ファイル
1巡目: 2.3 1.8 1.2 1.0 1.0 1.0 0.8 0.8 0.7 0.8
2巡目: 1.7 2.0 6.3 1.6 1.1 1.1 1.2 1.0 0.9 0.8
/bin/true(計測の下限)
1巡目: 1.8 1.1 0.6 0.8 3.7 0.5 0.4 0.5 0.5 0.4
2巡目: 0.5 1.0 1.0 0.7 0.8 0.6 0.5 0.4 0.4 0.4

図やコードに出てくる用語(タップで説明)