JVMが起動したときの Class ローディングと初期化について

# 概要

[HotSpot Runtime Overview](https://openjdk.java.net/groups/hotspot/docs/RuntimeOverview.html)にはこう書いています

> The VM loads core classes such as `java.lang.Object`, `java.lang.Thread`, etc. at JVM startup. Loading a class requires loading all superclasses and superinterfaces. And classfile verification, which is part of the linking phase, can require loading additional classes.

* JVMは起動したすぐ`java.lang.Object`、`java.lang.Thread`などの Class をロードする
* 依頼された super と interface は自動にロードする
* その他の Class は必要なときだけにロードする

じゃ具体的にはどんな Class をいつにロードされたのか？[Unified JVM Logging](https://openjdk.java.net/jeps/158) を利用して実際に見に行きましょう。

# 作業環境

[前の記事](https://qiita.com/stwind/items/4bec3755ef8c32df6fbe#%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89)との同じく Ubuntu 18.04 と OpenJDK 13 を使いています、具体的の構築方法はそちらに参照してください。

# `startuptime`を見てみる

まずは`-Xlog:startuptime`の変数でJVMの大まかの起動時間が見てみよう

```sh
$ $JAVA_HOME/bin/java -Xlog:startuptime -version
[0.168s][info][startuptime] StubRoutines generation 1, 0.0257810 secs
[0.229s][info][startuptime] Genesis, 0.0605841 secs
[0.232s][info][startuptime] TemplateTable initialization, 0.0010887 secs
[0.263s][info][startuptime] Interpreter generation, 0.0302811 secs
[0.693s][info][startuptime] StubRoutines generation 2, 0.0152926 secs
[0.697s][info][startuptime] MethodHandles adapters generation, 0.0017809 secs
[0.703s][info][startuptime] Start VMThread, 0.0010136 secs
[0.997s][info][startuptime] Initialize java.lang classes, 0.2923160 secs
[1.018s][info][startuptime] Initialize java.lang.invoke classes, 0.0159241 secs
[3.161s][info][startuptime] Initialize module system, 2.1431533 secs
[3.173s][info][startuptime] Create VM, 3.1052374 secs
openjdk version "13.0.3-internal" 2020-04-14
OpenJDK Runtime Environment (fastdebug build 13.0.3-internal+0-adhoc.root.jdk13u)
OpenJDK 64-Bit Server VM (fastdebug build 13.0.3-internal+0-adhoc.root.jdk13u, mixed mode)
```

ここでわかるのは`Start VMThread`のあどで幾つの classe と modules を初期化しました。

```
[0.997s][info][startuptime] Initialize java.lang classes, 0.2923160 secs
[1.018s][info][startuptime] Initialize java.lang.invoke classes, 0.0159241 secs
[3.161s][info][startuptime] Initialize module system, 2.1431533 secs
```

# Class の`load`と`init`を見る

次は`-Xlog:startuptime,class+init,class+load`の変数を設定してもう一度実行しましょう。

```sh
$ $JAVA_HOME/bin/java -Xlog:startuptime,class+init,class+load -version
[0.096s][info][class,load] path: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/modules/java.base
[0.159s][info][startuptime] StubRoutines generation 1, 0.0283598 secs
[0.220s][info][startuptime] Genesis, 0.0597463 secs
[0.223s][info][startuptime] TemplateTable initialization, 0.0012748 secs
[0.252s][info][startuptime] Interpreter generation, 0.0291667 secs
[0.284s][info][class,load ] path: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/modules/java.base
[0.285s][info][class,load ] path: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/modules/java.base
[0.314s][info][class,load ] java.lang.Object source: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/modules/java.base
[0.321s][info][class,load ] java.io.Serializable source: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/modules/java.base
...
[0.553s][info][class,load ] java.util.Iterator source: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/modules/java.base
[0.578s][info][class,load ] java.lang.NullPointerException source: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/modules/java.base
[0.579s][info][class,load ] java.lang.ArithmeticException source: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/modules/java.base
[0.619s][info][startuptime] StubRoutines generation 2, 0.0132195 secs
[0.622s][info][startuptime] MethodHandles adapters generation, 0.0018546 secs
[0.627s][info][startuptime] Start VMThread, 0.0007446 secs
[0.629s][info][class,init ] 0 Initializing 'java/lang/Object' (0x0000000100001080)
[0.644s][info][class,init ] 1 Initializing 'java/lang/CharSequence'(no method) (0x00000001000016b8)
...
[0.898s][info][class,init ] 135 Initializing 'java/lang/IllegalArgumentException'(no method) (0x000000010002b0e8)
[0.898s][info][startuptime] Initialize java.lang classes, 0.2690178 secs
[0.902s][info][class,init ] 136 Initializing 'java/lang/invoke/MethodHandle' (0x000000010000c560)
...
[0.925s][info][class,init ] 144 Initializing 'java/lang/invoke/MethodHandleNatives' (0x000000010000d2e8)
[0.925s][info][startuptime] Initialize java.lang.invoke classes, 0.0232579 secs
[0.935s][info][class,load ] jdk.internal.module.ModuleBootstrap source: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/modules/java.base
...
[3.112s][info][class,load ] jdk.internal.module.ModuleBootstrap$SafeModuleFinder source: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/modules/java.base
[3.113s][info][class,init ] 662 Initializing 'jdk/internal/module/ModuleBootstrap$SafeModuleFinder'(no method) (0x000000010008e930)
[3.113s][info][startuptime] Initialize module system, 2.1876125 secs
[3.124s][info][startuptime] Create VM, 3.0688743 secs
openjdk version "13.0.3-internal" 2020-04-14
OpenJDK Runtime Environment (fastdebug build 13.0.3-internal+0-adhoc.root.jdk13u)
OpenJDK 64-Bit Server VM (fastdebug build 13.0.3-internal+0-adhoc.root.jdk13u, mixed mode)
```

ここでわかったのは、実際には２つの段階があります

* `Interpreter generation`と`StubRoutines generation 2`の間に幾つの Class がロードされました。
* `Start VMThread`あどに 663 個（0-662）の Class が初期化されました
    * `java.lang` 136 個 (0-135)
    * `java.lang.invoke` 9 個 (136-144)
    * `module system` 518 個 (145-622)

# ソースコードで確認する

上記のテキストをソースコードに検索したら、この２つの段階を触発する場所がわかります。

* [`SystemDictionary::resolve_well_known_classes`](https://github.com/openjdk/jdk13u/blob/master/src/hotspot/share/classfile/systemDictionary.cpp#L1942): `SystemDictionary`はほぼJVMの Class 管理機能がしています。この`SystemDictionary`が初期化したときにいわゆる **Well Know Classes** というコアな Class たちも一緒に初期化する。Well Known Classes のリストは[ここ](https://github.com/openjdk/jdk13u/blob/master/src/hotspot/share/classfile/systemDictionary.hpp#L103)に記載してます、その順序は大事のようです。
* [`Threads::create_vm`](https://github.com/openjdk/jdk13u/blob/master/src/hotspot/share/runtime/thread.cpp#L3734): `Start VMThread`のあどに`java.lang classes`、`java.lang.invoke classes`と`module system`を一気に初期化するのはここです。具体的にはこれらの３つの関数で
    * [`Threads::initialize_java_lang_classes`](https://github.com/openjdk/jdk13u/blob/master/src/hotspot/share/runtime/thread.cpp#L3660)
    * [`Threads::initialize_jsr292_core_classes`](https://github.com/openjdk/jdk13u/blob/master/src/hotspot/share/runtime/thread.cpp#L3725)
    * [`call_initPhase2`](https://github.com/openjdk/jdk13u/blob/master/src/hotspot/share/runtime/thread.cpp#L3629)

# まとめ

JVMが起動したときにこれらのコアな Class を初期化します

* Well Know Classes (`SystemDictionary`の初期化とともに)
* `java.lang` Class
* `java.lang.invoke` Class
* `$JAVA_HOME/modules` の Class