クラスローディングはバイナリ表現を Class オブジェクトへ変換します。リンクと初期化
は別の段階であるため、型をロードしても静的初期化子が実行されたとは限りません。
ローダーと型の同一性 #
JDK はブートストラップ、プラットフォーム、アプリケーションのローダーを提供します。
ブートストラップローダーはコアプラットフォームクラスを定義し、一部の API では null
で表されます。プラットフォームローダーはプラットフォームモジュールを定義し、
アプリケーションローダーはクラスパスまたはモジュールパスからアプリケーションクラスを
ロードします。カスタムローダーは通常、親優先委譲を使用します。
- reuse an already loaded definition;
- ask the parent to load the binary name; then
- find bytes and call
defineClass.
親優先委譲はプラットフォームの型の同一性を保護します。ただし絶対的な規則ではなく、
プラグインシステムでは厳格なパッケージ除外とともに子優先ロードを使うことがあります。
実行時の型は、バイナリ名とそれを定義したローダーの両方で識別されます。そのため、
2 つのローダーが異なる com.example.Plugin 型を定義でき、それらのインスタンス間で
キャストすることはできません。
ロード、リンク、初期化 #
JVM 仕様はライフサイクルを次のように分けています。
- Loading はバイト列を取得し、
Classオブジェクトを作成し、定義ローダーを関連付けます。 - Linking には検証、準備、任意の解決が含まれます。検証は構造と型安全性の制約を確認します。準備は静的フィールドを割り当て、デフォルト値を設定します。解決はシンボリック参照を実行時参照へ変換しますが、実装はこれを遅延できます。
- Initialization は合成された
<clinit>メソッドを実行します。このメソッドには、ソース順の静的フィールド初期化子と静的ブロックが含まれます。
Compile-time constants are assigned by preparation, so reading a constant variable can avoid initialization of its declaring class. Do not infer a portable resolution schedule from an implementation’s logging output.
初期化が正確にトリガーされる条件 #
JLS §12.4.1 は、クラスの最初の能動的使用の直前に初期化することを要求します。能動的
使用には、インスタンスの作成、静的メソッドの呼び出し、静的フィールドへの代入、
定数変数でない静的フィールドの読み取りが含まれます。クラスを必要とする static
アサーションの実行、指定されたリフレクション操作、規定されたメソッドハンドルの
動作も含まれます。Class.forName はデフォルトで初期化します。初期化を要求せずに
ロードするには、3 引数オーバーロードで false を使用します。
クラス C が初期化される前に、そのスーパークラスが初期化され、該当する場合は JLS
で定義された順序でデフォルトメソッドを宣言するスーパーインターフェイスが続きます。
クラスを初期化しても、それが実装する任意のインターフェイスが初期化されるわけでは
ありません。Child.value を読み取ると、必ずしも Child ではなく、value を実際に
宣言するクラスが初期化されます。
Failure and Concurrency #
The JVM serializes initialization for each class or interface. Other threads
wait while one thread runs <clinit>; recursive requests by that same thread
are permitted. If initialization completes abruptly, the VM records the type
as erroneous, reports an initialization failure to the triggering use, and
later active uses fail with NoClassDefFoundError. Restarting the process or
using an independent loader is required to obtain another initialization
attempt.
Static initializers should establish simple, failure-free invariants. Network calls, lock acquisition, and configuration parsing can deadlock or permanently poison a type after one transient failure.
コメント
GitHub で閲覧・コメントする(投稿にはログインが必要です)