ネイティブ・イメージのクラスの初期化
Javaのセマンティクスにより、実行時にクラスに初めてアクセスが発生したときにそのクラスを初期化することが求められます。クラスの初期化は、JavaのAhead-of-Timeコンパイルに対して、次のような悪影響を及ぼします:
- ネイティブ・イメージのパフォーマンスが大幅に低下します: クラスへのアクセス(フィールドまたはメソッドを使用)が発生するたびに、クラスがすでに初期化されているかどうかをチェックする必要があります。特別な最適化を行わないと、パフォーマンスが2倍以上低下する可能性があります。
- これにより、アプリケーション起動時の処理量が増加します。たとえば、単純なHello, World!プログラムでは、300を超えるクラスの初期化が必要です。
クラスの初期化による悪影響を軽減するために、ネイティブ・イメージではビルド時におけるクラスの初期化がサポートされています。イメージのビルド中に特定のクラスを初期化して、実行時の初期化とチェックを不要にすることができます。初期化されたクラスのすべての静的状態情報がイメージに格納されます。ビルド時に初期化された静的フィールドへのアクセスは、アプリケーションに対して透過的であり、クラスが実行時に初期化されたかのように機能します。
クラス初期化ポリシーの指定は、クラス初期化セマンティクスに起因する次の制約のために複雑になる場合があります:
-
クラスを初期化するとき、デフォルト・メソッドを持つスーパークラスとスーパー・インタフェースもすべて初期化する必要があります。ただし、デフォルト・メソッドのないインタフェースは初期化されません。これを表すために、短期的な関連スーパータイプと、デフォルト・メソッドを持つクラスおよびインタフェースのサブタイプの関連サブタイプが使用されます。
- ビルド時に初期化される型の関連スーパータイプも、ビルド時に初期化する必要があります。
- 実行時に初期化される型の関連サブタイプも、実行時に初期化する必要があります。
- 実行時に初期化されるインスタンス・クラスは、イメージに存在しないようにする必要があります。
ネイティブ・イメージの操作性はそのまま保ちながらビルド時の初期化のメリットを得るために、ネイティブ・イメージでは次の3つのことが行われます:
初期化されたクラスとその理由を追跡するには、フラグ-H:+PrintClassInitialization
を使用します。このフラグは、イメージ・ビルドが意図したとおりに動作するように構成するために非常に役立ちます。目的は、プログラムの正しいセマンティクスを維持したまま、可能なかぎり多くのクラスをビルド時に初期化することです。
ネイティブ・イメージ・ランタイムのビルド時の初期化
ネイティブ・イメージ・ランタイムでは、ほとんどのクラスがイメージのビルド時に初期化されます。これには、ガベージ・コレクタ、重要なJDKクラス、デオプティマイザなどが含まれます。ネイティブ・イメージでは、ビルド時に初期化されるランタイム内のすべてのクラスを適切にサポートするために、ビルド時に初期化が発生してもセマンティクスは維持されるようになっています。ビルド時のクラスの初期化が原因でJDKクラスが正しく動作しない場合は、問題を報告してください。
安全なクラスの自動初期化
アプリケーション・クラスについては、ネイティブ・イメージにより、ビルド時に安全に初期化できるクラスの検出が試みられます。クラスは、そのすべての関連スーパータイプが安全で、かつ、クラス・イニシャライザが安全でないメソッドをコールすることや、他の安全でないクラスを初期化することがない場合、安全とみなされます。
メソッドは次の場合に安全でないとみなされます:
- ネイティブ・コードを推移的にコールする場合(
System.out.println
など): ネイティブ・コードは分析されないため、不正なアクションが実行されてもネイティブ・イメージには認識されません。 - 単一のターゲットに削減できないメソッド(仮想メソッド)がコールされる場合。この制限は、静的イニシャライザの安全性分析の対象となる検索領域が増大しないようにするためのものです。
- ネイティブ・イメージによって置き換えられる場合。置き換えられたメソッドのイニシャライザを実行すると、生成されたイメージとは異なる結果がホストVMで生成されます。その結果、安全性分析では一部のメソッドが安全であるとみなされますが、その実行は不正な状態になります。
安全であると実証されているクラスの例を示すテストは、ここで参照できます。コマンドラインで-H:+PrintClassInitialization
を設定すると、安全であると実証されているすべてのクラスのリストがファイルに表示されます。
クラスの初期化の明示的な指定
各クラスは実行時(1)に初期化することも、ビルド時(2)に初期化することもできます。クラス初期化ポリシーを指定するために、--initialize-at-build-time
と--initialize-at-run-time
の2つのフラグが用意されています。これらのフラグを使用すると、パッケージ全体または個々のクラスのポリシーを指定できます。たとえば、クラスp.C1
、p.C2
、...またはp.Cn
がある場合、次のようにしてこのパッケージを即時に初期化できます:
--initialize-at-build-time=p
パッケージp
内のクラスのいずれかを遅延させる場合は、単に、次の内容を追加します:
--initialize-at-run-time=p.C1
クラス階層全体をビルド時に初期化するには、コマンドラインで--initialize-at-build-time
を渡します。
ネイティブ・イメージ機能のRuntimeClassInitialization
を使用して、プログラムでクラスの初期化を指定することもできます。