ネイティブ・イメージのクラスの初期化
Javaのセマンティクスにより、実行時にクラスに初めてアクセスが発生したときにそのクラスを初期化することが求められます。クラスの初期化では、次の2つの理由により、Javaアプリケーションの事前コンパイルに対して悪影響があります:
- ネイティブ実行可能ファイルのパフォーマンスが大幅に低下します。クラスへのすべてのアクセス(フィールドまたはメソッド経由)には、クラスがすでに初期化されていることを確認するためのチェックが必要です。最適化を行わないと、パフォーマンスが2倍以上低下する可能性があります。
- アプリケーションを起動する計算量と時間が長くなります。たとえば、単純な"Hello, World!"アプリケーションでは、300を超えるクラスを初期化する必要があります。
クラス初期化の悪影響を減らすために、ネイティブ・イメージは、ビルド時にクラス初期化をサポートします。これは、実行可能ファイルをビルドするときにクラスを初期化し、実行時の初期化および不要なチェックを行います。初期化されたクラスのすべての静的状態が実行可能ファイルに格納されます。ビルド時に初期化されたクラスの静的フィールドへのアクセスは、アプリケーションに対して透過的であり、クラスが実行時に初期化されたかのように機能します。
ただし、Javaクラス初期化セマンティクスは、次のようなクラス初期化ポリシーを複雑にするいくつかの制約を課します:
-
クラスを初期化する場合は、デフォルト・メソッドを持つすべてのスーパークラスおよびスーパーインタフェースも初期化する必要があります。ただし、デフォルト・メソッドのないインタフェースは初期化されません。この要件に対応するために、短期的な「関連スーパータイプ」と、デフォルト・メソッドを持つクラスおよびインタフェースのサブタイプ用の「関連サブタイプ」が使用されます。
- ビルド時に初期化される型の関連スーパータイプも、ビルド時に初期化する必要があります。
- 実行時に初期化される型の関連サブタイプも、実行時に初期化する必要があります。
- 実行時に初期化されるクラスのインスタンスは、実行可能ファイルに存在する必要はありません。
ネイティブ・イメージの操作性はそのまま保ちながらビルド時の初期化のメリットを得るために、ネイティブ・イメージでは次の2つのことが行われます:
初期化されたクラスとその理由を追跡するには、コマンドライン・オプション-H:+PrintClassInitialization
をnative-image
ツールに渡します。このオプションは、必要に応じて動作するようにnative image
ビルダーを構成するのに役立ちます。目的は、アプリケーションの正しいセマンティクスを維持したまま、可能なかぎり多くのクラスをビルド時に初期化することです。
ビルド時の初期化
ネイティブ・イメージは、ガベージ・コレクタ、重要なJDKクラス、デオプティマイザなど、ほとんどのJDKクラスをビルド時に初期化します。ビルド時に初期化されるすべてのクラスについて、ネイティブ・イメージは適切なサポートを提供するため、ビルド時にクラスの初期化が行われたにもかかわらず、セマンティクスは一貫性が保たれます。ビルド時のクラスの初期化が原因でJDKクラスが正しく動作しない問題がある場合は、問題を報告してください。
安全なクラスの自動初期化
アプリケーション・クラスについては、ネイティブ・イメージにより、ビルド時に安全に初期化できるクラスの検出が試みられます。クラスは、そのすべての関連スーパータイプが安全で、かつ、クラス・イニシャライザが安全でないメソッドをコールすることや、他の安全でないクラスを初期化することがない場合、安全とみなされます。
次の場合、メソッドは安全でないとみなされます:
- ネイティブ・コード(
System.out.println
など)を推移的にコールする場合。ネイティブ・コードは分析されないため、不正なアクションが実行されるかどうかはネイティブ・イメージでは認識できません。 - 単一のターゲットに削減できないメソッド(仮想メソッド)がコールされる場合。この制限は、静的イニシャライザの安全性分析の対象となる検索領域が増大しないようにします。
- ネイティブ・イメージによって置き換えられる場合。置き換えられたメソッドのイニシャライザを実行すると、生成された実行可能ファイルとは異なる結果がホストJava仮想マシン(VM)で生成されます。その結果、安全性分析では一部のメソッドが安全であるとみなされますが、これをコールすると不正な状態になります。
安全であると実証されているクラスの例を示すテストは、ここで参照できます。安全であることが証明されたすべてのクラスのリストは、native-image
ツールの-H:+PrintClassInitialization
コマンドライン・オプションを使用してファイルに出力されます。
ノート: クラスの初期化を明示的に指定することもできます。