到達可能性メタデータ

JVMの動的言語機能(リフレクションやリソース処理など)によって、実行時にフィールド、メソッド、リソースURLなどの動的にアクセスされるプログラム要素が計算されます。HotSpotでは、すべてのクラス・ファイルおよびリソースが実行時に使用可能であり、ライタイムによってロードできるため、これが可能です。すべてのクラスおよびリソースの可用性と、実行時におけるそれらのロードには、メモリーと起動時間の追加のオーバーヘッドが伴います。

ネイティブ・バイナリを小さくするために、native-imageビルダーはビルド時に静的分析を実行して、アプリケーションの正確性に必要なプログラム要素のみを判別します。小さいバイナリでは、アプリケーションの起動が速くなり、メモリー・フットプリントが少なくなりますが、コストがかかります。静的分析を介して動的にアクセスされるアプリケーション要素を判別することは、それらの要素の到達可能性が実行時にのみ使用可能なデータに依存するため、実行不可能です。

動的にアクセスされる必要な要素をネイティブ・バイナリに確実に含めるには、native-imageビルダーに到達可能性メタデータ(これ以降、メタデータと呼びます)が必要です。ビルダーに正確で包括的な到達可能性メタデータを提供することで、アプリケーションの正確性が保証され、実行時にサードパーティ・ライブラリとの互換性が確保されます。

メタデータは、次の方法でnative-imageビルダーに提供できます:

ノート: ネイティブ・イメージは、到達可能性メタデータのよりユーザーフレンドリな実装に移行しており、早い段階で問題が表示され、デバッグが容易になります。

アプリケーションで新しいユーザーフレンドリな到達可能性メタデータ・モードを有効にするには、ビルド時にオプション--exact-reachability-metadataを渡します。具象パッケージに対してのみユーザーフレンドリ・モードを有効にするには、--exact-reachability-metadata=<comma-separated-list-of-packages>を渡します。

正確な動作にコミットせずに、登録の欠落が発生しているコード内のすべての場所を全体的に把握するには、アプリケーションの起動時に-XX:MissingRegistrationReportingMode=Warnを渡します。

アプリケーションが(catch (Throwable t)ブロックで)登録の欠落エラーを誤って無視している場所を検出するには、アプリケーションの起動時に-XX:MissingRegistrationReportingMode=Exitを渡します。その後、アプリケーションはスタック・トレースとともにエラー・メッセージを無条件に出力し、即座に終了します。この動作は、すべてのメタデータが含まれることを保証するアプリケーション・テストの実行に最適です。

リフレクションのユーザーフレンドリな実装は、GraalVMの将来のリリースでデフォルトになるため、プロジェクトの破損を回避するために適切なタイミングで採用することが重要です。

目次

コードでのメタデータの計算

コードでのメタデータの計算は、次の2つの方法で実現できます:

  1. JVMの要素に動的にアクセスする関数に定数引数を指定します。たとえば、次のコードのClass#forNameを参照してください:

     class ReflectiveAccess {
         public Class<Foo> fetchFoo() throws ClassNotFoundException {
             return Class.forName("Foo");
         }
     }
    

    ここで、Class.forName("Foo")はビルド時に定数に評価されます。ネイティブ・バイナリがビルドされると、この値はその初期ヒープに格納されます。クラスFooが存在しない場合、Class#forNameへのコールはthrow ClassNotFoundException("Foo")に変換されます。

    定数は次のように定義されます:

    • リテラル(たとえば、"Foo"または1)。
    • ビルド時に初期化される静的フィールドへのアクセス。
    • 実質的final変数へのアクセス。
    • 長さが定数で、すべての値が定数である配列の定義。
    • 他の定数に対する単純な計算(たとえば、"F" + "oo"または配列への索引付け)。

    定数配列を渡す場合、配列を宣言および移入する次のアプローチは、native-imageビルダーの観点からは同等です:

      Class<?>[] params0 = new Class<?>[]{String.class, int.class};
      Integer.class.getMethod("parseInt", params0);
    
      Class<?>[] params1 = new Class<?>[2];
      params1[0] = Class.forName("java.lang.String");
      params1[1] = int.class;
      Integer.class.getMethod("parseInt", params1);
    
      Class<?>[] params2 = {String.class, int.class};
      Integer.class.getMethod("parseInt", params2);
    

    現在、ネイティブ・イメージは定数を積極的に計算するため、ビルド時に定数の正確な内容を指定することはできません。

  2. ビルド時にクラスを初期化し、動的にアクセスされる要素をネイティブ実行可能ファイルの初期ヒープに格納します。このメタデータの提供方法は、定数またはJSONでメタデータを指定できない場合に適しています。これは次の場合に必要です:

    • ユーザー・コードは、新しいクラス・バイトコードを生成する必要があります。
    • ユーザー・コードは、アプリケーションに必要な動的にアクセスされるプログラム要素を計算するために、クラスパスをトラバースする必要があります。

    次の例では、

     class InitializedAtBuildTime {
         private static Class<?> aClass;
         static {
             try {
                 aClass = Class.forName(readFile("class.txt"));
             } catch (ClassNotFoundException e) {
                 throw RuntimeException(e);
             }
         }
    
         public Class<?> fetchFoo() {
             return aClass;
         }
     }
    

動的にアクセスされる要素は、ヒープのその部分が包含メソッド(InitializedAtBuildTime#fetchFooなど)または静的フィールド(InitializedAtBuildTime.aClassなど)を介してアクセス可能な場合にのみ、ネイティブ実行可能ファイルのヒープに含まれます。

JSONを使用したメタデータの指定

すべてのメタデータは、META-INF/native-image/<groupId>/<artifactId>/のいずれかのクラスパス・エントリにあるreachability-metadata.jsonファイルに指定されています。到達可能性メタデータのJSONスキーマは、reachability-metadata-schema-v1.0.0.jsonに定義されています。

サンプルのreachability-metadata.jsonファイルは、サンプルの項にあります。reachability-metadata.json構成には、メタデータのタイプごとに1つのフィールドを持つ単一のオブジェクトが含まれています。最上位オブジェクト内の各フィールドには、メタデータ・エントリの配列が含まれます:

{
  "reflection":[],
  "resources":[],
  "bundles":[],
  "serialization":[],
  "jni":[]
}

たとえば、Javaリフレクション・メタデータはreflectionで指定され、サンプル・エントリは次のようになります:

{
  "reflection": [
    {
      "type": "Foo"
    }
  ]
}

条件付きメタデータ・エントリ

JSONベースのメタデータ内の各エントリは、ネイティブ・バイナリのサイズが不必要に大きくならないように条件付きである必要があります。条件付きエントリは、次のようにエントリにconditionフィールドを追加することによって指定されます:

{
  "condition": {
    "typeReached": "<fully-qualified-class-name>"
  },
  <metadata-entry>
}

typeReached条件を持つメタデータ・エントリは、指定した完全修飾型に実行時に到達した場合にのみ、実行時に使用可能とみなされます。その前は、metadata-entryで表される要素へのすべての動的アクセスは、metadata-entryが存在しないかのように動作します。つまり、これらの動的アクセスによって、登録の欠落エラーがスローされます。

実行時に型に到達すると、その直後にその型(クラスまたはインタフェース)のクラス初期化ルーチンが開始されるか、型のサブタイプのいずれかに到達します。次の例でメタデータ・エントリを保護している"typeReached": "ConditionType"については、その型に到達したとみなされます:

class SuperType {
    static {
        // ConditionType reached (subtype reached) => metadata entry available
    }
}
class ConditionType extends SuperType {
    static {
        // ConditionType reached (before static initializer) => metadata entry available
    }
    static ConditionType singleton() {
        // ConditionType reached (already initialized) => metadata entry available
    }
}
public class App {
    public static void main(String[] args) {
        // ConditionType not reached => metadata entry not available
        ConditionType.class;
        // ConditionType not reached (ConditionType.class doesn't start class initialization) => metadata entry not available  
        ConditionType.singleton();
        // ConditionType reached (already initialized) => metadata entry available
    }
}

initialize-at-build-timeとマークされている場合、またはそのサブタイプのいずれかがinitialize-at-build-timeとマークされ、クラスパスに存在する場合も、型に到達します。

配列型は到達済としてマークされないため、条件では使用できません。

ビルド時に完全修飾型に到達可能な場合は、条件付きメタデータ・エントリがイメージに含まれます。このエントリはイメージ・サイズに影響し、実行時に条件に達した場合にのみ使用可能になります。

メタデータ・ファイルのその他の例は、GraalVM到達可能性メタデータ・リポジトリを参照してください。

メタデータ・タイプ

ネイティブ・イメージは、次のタイプの到達可能性メタデータを受け入れます:

リフレクション

このセクションのすべてのメソッドについて、ネイティブ・イメージは、すべてのコール引数が定数である場合、ビルド時に到達可能性を計算します。コードに定数引数を指定することは、外部JSONファイルの情報の複製を必要としないため、メタデータを提供するために推奨される方法です。

Javaのリフレクションは、メソッドやフィールドなどの追加のリフレクティブ要素をフェッチできるjava.lang.Classで始まります。クラスは、java.lang.Classの次の静的関数を介してリフレクティブにフェッチできます:

Classをリフレクティブにフェッチするコールにメタデータを提供するには、reachability-metadata.jsonreflection配列に次のエントリを追加する必要があります:

{
  "type": "FullyQualifiedReflectivelyAccessedType"
}

プロキシ・クラスの場合、java.lang.Classは、java.lang.reflect.Proxyの次のメソッドを使用してフェッチされます:

プロキシ・クラスのメタデータは、プロキシを定義するインタフェースの順序付けされたコレクションの形式です:

{
  "type": {
    "proxy": ["FullyQualifiedInterface1", "...", "FullyQualifiedInterfaceN"]
  }
}

前述のメソッドを、指定されたメタデータなしで呼び出すと、java.lang.Errorを拡張するMissingReflectionRegistrationErrorがスローされ、処理できません。クラスパスに型が存在しない場合でも、前述のメソッドはMissingReflectionRegistrationErrorをスローすることに注意してください。

java.lang.Classの次のメソッドは、指定されたタイプにメタデータが指定されていない場合、MissingRegistrationErrorをスローします:

さらに、java.lang.invoke.MethodHandles.Lookupを介したすべてのリフレクティブ・ルックアップでは、型が存在するためのメタデータも必要です。それがないと、MissingReflectionRegistrationErrorがスローされます。

ラムダ・プロキシ・クラスでは、メタデータを指定できないことに注意してください。これは既知の問題であり、GraalVMの将来のリリースで対処される予定です。

リフレクティブなメソッド呼出し

メソッドをリフレクティブに呼び出すには、メソッド・シグネチャをtypeメタデータに追加する必要があります:

{
  "type": "TypeWhoseMethodsAreInvoked",
  "methods": [
    {"name": "<methodName1>", "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]},
    {"name": "<methodName2>", "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]}
  ]
}

便宜上、reachability-metadata.jsonに次を追加することで、メソッド・グループに対するメソッド呼出しを許可できます:

{
  "type": "TypeWhoseMethodsAreInvoked",
  "allDeclaredConstructors": true,
  "allPublicConstructors": true,
  "allDeclaredMethods": true,
  "allPublicMethods": true
}

allDeclaredConstructorsおよびallDeclaredMethodsは、指定された型で宣言されたメソッドの呼出しを許可します。allPublicConstructorsおよびallPublicMethodsは、型およびそのすべてのスーパータイプで定義されたすべてのパブリック・メソッドの呼出しを許可します。

メソッド呼出しのメタデータがない場合、次のメソッドはMissingReflectionRegistrationErrorをスローします:

リフレクティブなフィールド値アクセス

フィールド値にリフレクティブにアクセス(取得または設定)するには、フィールド名に関するメタデータを型に追加する必要があります:

{
  "type": "TypeWhoseFieldValuesAreAccessed",
  "fields": [{"name": "<fieldName1>"}, {"name": "<fieldNameI>"}, {"name": "<fieldNameN>"}]
}

便宜上、reachability-metadata.jsonに次を追加することで、すべてのフィールドに対するフィールド値アクセスを許可できます:

{
  "type": "TypeWhoseFieldValuesAreAccessed",
  "allDeclaredFields": true,
  "allPublicFields": true
}

allDeclaredFieldsは、指定された型で宣言されたすべてのフィールドへのアクセスを許可します。allPublicFieldsは、指定された型およびそのすべてのスーパータイプのすべてのパブリック・フィールドへのアクセスを許可します。

フィールド値アクセスのメタデータがない場合、次のメソッドはMissingReflectionRegistrationErrorをスローします:

型の安全でない割当て

sun.misc.Unsafe#allocateInstance(Class<?>)を介して、またはネイティブ・コードからAllocObject(jClass)を介して、型の安全でない割当てを行うには、次のメタデータを指定する必要があります:

{
  "type": "FullyQualifiedUnsafeAllocatedType",
  "unsafeAllocated": true
}

そうしないと、これらのメソッドはMissingReflectionRegistrationErrorをスローします。

リフレクション・メタデータのサマリー

JSONの型の全体的な定義は、次の値のようになります:

{
  "condition": {
    "typeReached": "<condition-class>"
  },
  "type": "<class>|<proxy-interface-list>",
  "fields": [
    {"name": "<fieldName>"}
  ],
  "methods": [
    {"name": "<methodName>", "parameterTypes": ["<param-type>"]}
  ],
  "allDeclaredConstructors": true,
  "allPublicConstructors": true,
  "allDeclaredMethods": true,
  "allPublicMethods": true,
  "allDeclaredFields": true,
  "allPublicFields": true,
  "unsafeAllocated": true
}

Java Native Interface

Java Native Interface (JNI)を使用すると、ネイティブ・コードは任意のJavaタイプおよびタイプ・メンバーにアクセスできます。ネイティブ・イメージでは、このようなネイティブ・コードのルックアップ、書込みまたは呼出しを予測できません。JNIを使用してJava値にアクセスするJavaアプリケーションのネイティブ・バイナリをビルドするには、JNIメタデータが必要です。

たとえば、次のCコードの場合:

jclass clazz = FindClass(env, "jni/accessed/Type");

jni.accessed.Typeクラスをルックアップし、このクラスを使用してjni.accessed.Typeのインスタンス化、メソッドの呼出し、またはフィールドへのアクセスを行うことができます。

前述のコールに対するメタデータ・エントリは、reachability-metadata.jsonを介してのみ指定できます。jniフィールドにtypeエントリを指定します:

{
  "jni":[
    {
      "type": "jni.accessed.Type"
    }
  ]
}

型のメタデータを追加しても、GetFieldIDGetStaticFieldIDGetStaticMethodIDおよびGetMethodIDを使用してすべてのフィールドおよびメソッドをフェッチすることはできません。

フィールド値にアクセスするには、フィールド名を指定する必要があります:

{
  "type": "jni.accessed.Type",
  "fields": [{"name": "value"}]
}

すべてのフィールドにアクセスするには、次の属性を使用します:

{
  "type": "jni.accessed.Type",
  "allDeclaredFields": true,
  "allPublicFields": true
}

allDeclaredFieldsは、指定された型で宣言されたすべてのフィールドへのアクセスを許可します。allPublicFieldsは、指定された型およびそのすべてのスーパータイプのすべてのパブリック・フィールドへのアクセスを許可します。

JNIからJavaメソッドをコールするには、メソッド・シグネチャのメタデータを指定する必要があります:

{
  "type": "jni.accessed.Type",
  "methods": [
    {"name": "<methodName1>", "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]},
    {"name": "<methodName2>", "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]}
  ]
}

便宜上、次を追加することで、メソッド・グループに対するメソッド呼出しを許可できます:

{
  "type": "jni.accessed.Type",
  "allDeclaredConstructors": true,
  "allPublicConstructors": true,
  "allDeclaredMethods": true,
  "allPublicMethods": true
}

allDeclaredConstructorsおよびallDeclaredMethodsは、指定された型で宣言されたメソッドの呼出しを許可します。allPublicConstructorsおよびallPublicMethodsは、型およびそのすべてのスーパータイプで定義されたすべてのパブリック・メソッドの呼出しを許可します。

AllocObjectを使用して型のオブジェクトを割り当てるには、メタデータをreflectionセクションに格納する必要があります:

{
  "reflection": [
    {
      "type": "jni.accessed.Type",
      "unsafeAllocated": true
    }
  ]
}

ネイティブ・コードから動的にアクセスされる要素にメタデータを指定できないと、例外(MissingJNIRegistrationError)が発生します。

JNIを使用するほとんどのライブラリは例外を適切に処理しないため、どの要素が欠落しているかを確認するには、-XX:MissingRegistrationReportingMode=Warn--exact-reachability-metadataを組み合せて使用する必要があります。

リソース

Javaは、アプリケーション・クラスパスの任意のリソース、またはリクエスト元のコードがアクセス権限を持つモジュール・パスにアクセスできます。リソース・メタデータは、指定されたリソースおよびリソース・バンドルを生成されたバイナリに含めるようにnative-imageビルダーに指示します。このアプローチの結果、構成にリソースを使用するアプリケーションの一部(ロギングなど)は、ビルド時に効果的に構成されます。

ネイティブ・イメージは、次のようなjava.lang.Class#getResourceおよびjava.lang.Class#getResourceAsStreamへのコールを検出します:

次のコードは、何も設定しなくてもそのまま機能します。理由は次のとおりです:

JSONでのリソース・メタデータ

リソース・メタデータは、reachability-metadata.jsonファイルのresourcesフィールドで指定されます。リソース・メタデータの例を次に示します:

{
  "resources": [
    {
      "glob": "path1/level*/**"
    }
  ]
}

globフィールドは、リソースを指定するためにglobパターン・ルールのサブセットを使用します。リソース・パスを指定する際には、いくつかのルールに従う必要があります:

次のプロジェクト構造があるとします:

app-root
└── src
    └── main
        └── resources
            ├── Resource0.txt
            └── Resource1.txt

次のことが可能です:

Javaモジュールのリソース

リソースまたはリソース・バンドルごとに、リソースまたはリソース・バンドルの取得元のモジュールを指定できます。各エントリの個別のmoduleフィールドにモジュール名を指定できます。たとえば:

{
   "resources": [
      {
        "module:": "library.module",
        "glob": "resource-file.txt" 
      }
   ]
}

これにより、native-imageツールにJavaモジュールlibrary.moduleresource-file.txtのみが含まれるようになります。他のモジュールまたはクラスパスにパターンresource-file.txtに一致するリソースが含まれている場合、library-module内のリソースのみが、ネイティブ実行可能ファイルに含まれるように登録されます。また、ネイティブ・イメージでは、モジュールが実行時にアクセス可能であることが保証されます。

次のコード・パターンの場合:

InputStream resource = ModuleLayer.boot().findModule("library.module").getResourceAsStream(resourcePath);

これは、前述のように登録されたリソースに対して常に期待どおりに機能します(モジュールに静的分析で到達可能と思われるコードが含まれていない場合でも)。

埋込みリソース情報

ネイティブ実行可能ファイルに含まれていたリソースを確認するには、2つの方法があります:

  1. ネイティブ実行可能ファイルのビルド・レポートを生成するには、オプション--emit build-reportを使用します。含まれているすべてのリソースに関する情報は、その「Resources」タブにあります。
  2. 含まれるすべてのリソースをリストするJSONファイルembedded-resources.jsonを生成するには、オプション-H:+GenerateEmbeddedResourcesFileを使用します。

登録されたリソースごとに次が取得されます:

ノート: リソース・ディレクトリのサイズは、すべてのディレクトリ・エントリの名前のサイズのみを表します(コンテンツ・サイズの合計ではありません)。

リソース・バンドル

Javaローカライゼーション・サポート(java.util.ResourceBundle)では、L10Nリソースをロードして、特定のロケール用にローカライズされたメッセージを表示できます。アプリケーションに適切なリソースおよびプログラム要素を含めることができるように、ネイティブ・イメージには、アプリケーションが使用するリソース・バンドルに関する知識が必要です。

単純なバンドルは、reachability-metadata.jsonbundlesセクションで指定できます:

{
  "bundles": [
    {
      "name":"your.pkg.Bundle"
    }
  ]
}

特定のモジュールからバンドルをリクエストするには:

{
  "bundles": [
    {
      "name":"app.module:module.pkg.Bundle"
    }
  ]
}

デフォルトでは、リソース・バンドルは、イメージに含まれるすべてのロケールに対して含まれます。次に、バンドルの特定のロケールのみを含める方法の例を示します:

{
  "bundles": [
    {
      "name": "specific.locales.Bundle",
      "locales": ["en", "de", "sk"]
    }
  ]
}

ロケール

ネイティブ実行可能ファイルに含めるロケールと、デフォルトにするロケールを指定することもできます。たとえば、デフォルトのロケールをスイス・ドイツ語に切り替え、フランス語と英語を含めるには、次のオプションを使用します:

native-image -Duser.country=CH -Duser.language=de -H:IncludeLocales=fr,en

ロケールは言語タグを使用して指定します。-H:+IncludeAllLocalesを介してすべてのロケールを含めることができますが、結果の実行可能ファイルのサイズが大きくなることに注意してください。

シリアライズ

Javaでは、Serializableインタフェースを実装する任意のクラスをシリアライズ(またはデシリアライズ)できます。ネイティブ・イメージは、適切なシリアライズ・メタデータ登録によるシリアライズ(またはデシリアライズ)をサポートします。これは、シリアライズでは通常、シリアライズされるオブジェクトにリフレクティブにアクセスする必要があるため必要です。

コードでのシリアライズ・メタデータの登録

ネイティブ・イメージはObjectInputFilter.Config#createFilter(String pattern)へのコールを検出し、pattern引数が定数である場合、パターンで指定されたクラスがシリアライズ用に登録されます。たとえば、次のパターンは、シリアライズ用にクラスpkg.SerializableClassを登録します:

  var filter = ObjectInputFilter.Config.createFilter("pkg.SerializableClass;!*;")
  objectInputStream.setObjectInputFilter(proof);

このパターンを使用すると、objectInputStreamで受け取ることができるのはpkg.SerializableClassのみになるため、JVMのセキュリティが向上するというプラスの効果があります。

ワイルドカード・パターンは、エンクロージング・クラスのラムダ・プロキシ・クラスに対してのみシリアライズ登録を行います。たとえば、エンクロージング・クラスpkg.LambdaHolderでラムダ・シリアライズを登録するには、次を使用します:

  ObjectInputFilter.Config.createFilter("pkg.LambdaHolder$$Lambda*;")

"pkg.**""pkg.Prefix*"などのパターンは、あまりにも一般的で、イメージ・サイズが大きくなるため、シリアライズ登録は実行されません。

sun.reflect.ReflectionFactory#newConstructorForSerialization(java.lang.Class)およびsun.reflect.ReflectionFactory#newConstructorForSerialization(java.lang.Class)のコールの場合、ネイティブ・イメージは、すべての引数とレシーバが定数の場合にこれらの関数のコールを検出します。たとえば、次のコールはシリアライズ用にSerializlableClassを登録します:

  ReflectionFactory.getReflectionFactory().newConstructorForSerialization(SerializableClass.class);

シリアライズ用のカスタム・コンストラクタを作成するには、次を使用します:

  var constructor = SuperSuperClass.class.getDeclaredConstructor();
  var newConstructor = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(BaseClass.class, constructor);

プロキシ・クラスは、JSONファイルを介してのみシリアライズに登録できます。

JSONでのシリアライズ・メタデータ

シリアライズ・メタデータは、reachability-metadata.jsonserializationセクションで指定されます。

通常のserialized.Typeを指定するには、次を使用します

{
  "serialization": [
    {
      "type": "serialized.Type"
    }
  ]
}

シリアライズのプロキシ・クラスを指定するには、次のエントリを使用します:

{
  "serialization": [
    {
      "type": {
        "proxy": ["FullyQualifiedInterface1", "...", "FullyQualifiedInterfaceN"]
      }
    }
  ]
}

まれに、アプリケーションが次を明示的にコールする場合があります:

    ReflectionFactory.newConstructorForSerialization(Class<?> cl, Constructor<?> constructorToCall);

ここで渡されるconstructorToCallは、clの通常のシリアライズの場合に自動的に使用されるものとは異なります。

このようなシリアライズのユース・ケースもサポートするために、カスタムconstructorToCallを使用してクラスのシリアライズを登録できます。たとえば、org.apache.spark.SparkContext$$anonfun$hadoopFile$1のシリアライズを可能にするには、java.lang.Objectの宣言されたコンストラクタをカスタムtargetConstructorとして使用します:

{
  "serialization": [
    {
      "type": "<fully-qualified-class-name>",
      "customTargetConstructorClass": "<custom-target-constructor-class>"
    }
  ]
}

サンプルの到達可能性メタデータ

reachabilty-metadata.jsonで使用できるサンプルの到達可能性メタデータ構成を次に示します:

{
  "reflection": [
    {
      "type": "reflectively.accessed.Type",
      "fields": [
        {
          "name": "field1"
        }
      ],
      "methods": [
        {
          "name": "method1",
          "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"] 
        }
      ],
      "allDeclaredConstructors": true,
      "allPublicConstructors": true,
      "allDeclaredFields": true,
      "allPublicFields": true,
      "allDeclaredMethods": true,
      "allPublicMethods": true,
      "unsafeAllocated": true
    }
  ],
  "jni": [
    {
      "type": "jni.accessed.Type",
      "fields": [
        {
          "name": "field1"
        }
      ],
      "methods": [
        {
          "name": "method1",
          "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]
        }
      ],
      "allDeclaredConstructors": true,
      "allPublicConstructors": true,
      "allDeclaredFields": true,
      "allPublicFields": true,
      "allDeclaredMethods": true,
      "allPublicMethods": true
    }
  ],
  "resources": [
    {
      "module": "optional.module.of.a.resource",
      "glob": "path1/level*/**"
    }
  ],
  "bundles": [
    {
      "name": "fully.qualified.bundle.name",
      "locales": ["en", "de", "other_optional_locales"]
    }
  ],
  "serialization": [
    {
      "type": "serialized.Type",
      "customTargetConstructorClass": "optional.serialized.super.Type"
    }
  ]
}

実行時のクラスの定義

Javaでは、実行時にバイトコードから新しいクラスをロードすることがサポートされていますが、ネイティブ・イメージでは、すべてのクラスがビルド時に認識されている必要があるためこれは不可能です(「閉世界仮説」)。この問題を回避するため、次のオプションがあります:

  1. アプリケーション(またはサードパーティ・ライブラリ)を変更または再構成して、実行時にクラスを生成したり、非組込みクラス・ローダーを介してクラスをロードしたりしないようにします。
  2. クラスを生成する必要がある場合は、専用クラスの静的イニシャライザでビルド時にクラスを生成してください。生成されたjava.lang.Classオブジェクトは、ビルド引数として--initialize-at-build-time=<class_name>を渡すことで初期化される静的フィールドおよび専用クラスに格納する必要があります。
  3. 前述のいずれも適用できない場合は、ネイティブ・イメージ・エージェントを使用してアプリケーションを実行し、java -agentlib:native-image-agent=config-output-dir=<config-dir>,experimental-class-define-support <application-arguments>を使用して事前定義済クラスを収集します。実行時に、トレース中に検出されたクラスの1つと同じ名前およびバイトコードを持つクラスをロードしようとすると、事前定義済のクラスがアプリケーションに提供されます。

事前定義済のクラス・メタデータは、predefined-classes-config.jsonファイルで指定され、predefined-classes-config-schema-v1.0.0.jsonで定義されているJSONスキーマに準拠します。スキーマには、この構成がどのように機能するかの詳細および説明も含まれます。次に、predefined-classes-config.jsonの例を示します。

[
  {
    "type": "agent-extracted",
    "classes": [
      {
        "hash": "<class-bytecodes-hash>",
        "nameInfo": "<class-name"
      }
    ]
  }
]

ノート: 事前定義済クラスのメタデータは、手動で記述することを意図していません。ノート: 事前定義済クラスは、レガシー・プロジェクト用のベストエフォート・アプローチであり、動作が保証されていません。

その他の情報