トレース・エージェントを使用したメタデータの収集

ネイティブ・イメージ・ツールは、実行時にアプリケーションのアクセス可能なコードの静的分析に依存します。ただし、分析では、Java Native Interface (JNI)、Javaリフレクション、動的プロキシ・オブジェクトまたはクラスパス・リソースのすべての使用状況を常に完全に予測できるわけではありません。これらの動的機能の検出されない使用状況は、メタデータ(コードまたはJSON構成ファイルで事前に計算)の形式でnative-imageツールに提供する必要があります。

ここでは、アプリケーションのメタデータを自動的に収集し、JSON構成ファイルを書き込む方法について説明します。コードで動的機能コールを計算する方法を学習するには、到達可能性メタデータを参照してください。

目次

トレース・エージェント

GraalVMには、メタデータを簡単に収集して構成ファイルを準備するためのトレース・エージェントが用意されています。エージェントは、通常のJava VMでのアプリケーション実行中に動的機能の使用をすべて追跡します。

GraalVM JDKのjavaコマンドを使用して、コマンドラインでエージェントを有効にします:

$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/ ...

-agentlibは、javaコマンドラインで-jarオプション、クラス名またはアプリケーション・パラメータのに指定する必要があります。

実行時に、エージェントは、native-imageツールで追加情報が必要なクラス、メソッド、フィールドおよびリソースを検索します。アプリケーションが完了してJVMが終了すると、エージェントは指定された出力ディレクトリ(/path/to/config-dir/)のJSONファイルにメタデータを書き込みます。

動的機能のカバレッジを向上させるために、アプリケーションを複数回(異なる実行パスで)実行することが必要な場合があります。config-merge-dirオプションは、次のように既存の一連の構成ファイルに追加されます:

$JAVA_HOME/bin/java -agentlib:native-image-agent=config-merge-dir=/path/to/config-dir/ ...                                                              ^^^^^

エージェントには、メタデータを定期的に書き込むための次のオプションもあります:

たとえば:

$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/,config-write-period-secs=300,config-write-initial-delay-secs=5 ...

前述のコマンドは、初期遅延の5秒後に300秒ごとにメタデータ・ファイルを/path/to/config-dir/に書き込みます。

生成された構成ファイルを手動で確認することをお薦めします。エージェントは実行されたコードのみを監視するため、アプリケーション入力はできるだけ多くのコード・パスをカバーする必要があります。

生成された構成ファイルは、クラスパスのMETA-INF/native-image/ディレクトリに配置することで、native-imageツールに渡すことができます。このディレクトリ(またはそのサブディレクトリ)では、jni-config.jsonreflect-config.jsonproxy-config.jsonresource-config.jsonpredefined-classes-config.jsonserialization-config.jsonという名前のファイルが検索され、自動的にビルド・プロセスに組み込まれます。これらのファイルがすべて存在する必要はありません。同じ名前のファイルが複数見つかった場合は、それらがすべて考慮されます。

サンプル・アプリケーションでメタデータを収集するエージェントをテストするには、リフレクションを使用したネイティブ実行可能ファイルのビルド・ガイドに移動します。

条件付きメタデータ・コレクション

エージェントは、実行されたコードの使用状況に基づいてメタデータの条件を推測できます。条件付きメタデータは、全体的なフットプリントを削減することを目的として、主にライブラリ・メンテナンスを対象としています。

エージェントを使用して条件付きメタデータを収集するには、条件付きメタデータ・コレクションを参照してください。

エージェントの高度な使用方法

コール元ベースのフィルタ

デフォルトでは、エージェントは、ネイティブ・イメージが構成なしでサポートする動的アクセスをフィルタ処理します。フィルタ・メカニズムでは、アクセスを実行するJavaメソッド(コール元メソッドとも呼ばれる)が識別され、その宣言クラスが一連のフィルタ・ルールと照合されます。組込みフィルタ・ルールでは、生成された構成ファイルから、JVMからの動的アクセス、またはネイティブ・イメージで直接サポートされているJavaクラス・ライブラリの一部(java.nioなど)からの動的アクセスが除外されます。アクセス対象の項目の種類(クラス、メソッド、フィールド、リソースなど)はフィルタ処理には関係ありません。

組込みフィルタに加えて、caller-filter-fileオプションを使用して、追加のルールを含むカスタム・フィルタ・ファイルを指定できます。たとえば、-agentlib:caller-filter-file=/path/to/filter-file,config-output-dir=...のように指定します

フィルタ・ファイルの構造は次のとおりです:

{ "rules": [
    {"excludeClasses": "com.oracle.svm.**"},
    {"includeClasses": "com.oracle.svm.tutorial.*"},
    {"excludeClasses": "com.oracle.svm.tutorial.HostedHelper"}
  ],
  "regexRules": [
    {"includeClasses": ".*"},
    {"excludeClasses": ".*\\$\\$Generated[0-9]+"}
  ]
}

rulesセクションには、一連のルールを指定します。各ルールでは、includeClasses (一致するクラスで発生したルックアップが結果の構成に追加される)またはexcludeClasses (一致するクラスで発生したルックアップが構成から除外される)を指定します。各ルールは、クラスを照合するパターンを定義します。パターンは、末尾を.*または.**とすることができ、次のように解釈されます: - .*は、該当するパッケージ内のすべてのクラスとそのパッケージのみと照合します。- .**は、該当するパッケージ内のすべてのクラスだけでなく、任意の深さのすべてのサブパッケージ内のすべてのクラスと照合します。.*または.**を指定しない場合、ルールはパターンと一致する修飾名を持つ単一のクラスにのみ適用されます。すべてのルールは指定された順序で処理されるため、後のルールによって前のルールの一部または全部をオーバーライドできます。複数のフィルタ・ファイルが指定されている(複数のcaller-filter-fileオプションを指定することによって)場合、それらのルールはファイルが指定されている順序で連鎖されます。組込みのコール元フィルタのルールは常に最初に処理されるため、カスタム・フィルタ・ファイルでオーバーライドできます。

前述の例では、最初のルールにより、生成されたメタデータから、パッケージcom.oracle.svmおよびそのすべてのサブパッケージ(さらにそれらのサブパッケージ)に含まれているすべてのクラスで発生したルックアップが除外されます。ただし、次のルールでは、パッケージcom.oracle.svm.tutorialに直接含まれているクラスからのルックアップが再度追加されます。最後に、HostedHelperクラスからのルックアップが再度除外されます。これらの各ルールは、前のルールを部分的にオーバーライドします。たとえば、ルールが逆順であった場合、com.oracle.svm.**の除外が最後のルールとなり、他のすべてのルールがオーバーライドされます。

regexRulesセクションにも、一連のルールを指定します。この構造はrulesセクションの構造と同じですが、ルールは、完全修飾クラス識別子全体と照合される正規表現パターンとして指定されます。regexRulesセクションはオプションです。regexRulesセクションが指定されている場合、rulesregexRulesの両方にクラスが含まれていて、いずれのクラスも除外されていない場合のみ、クラスは含まれるとみなされます。regexRulesセクションがない場合、rulesセクションのみがクラスを含めるか除外するかを決定します。

テスト目的で、no-builtin-caller-filterオプションを追加することでJavaクラス・ライブラリのルックアップの組込みフィルタを無効にできますが、結果のメタデータ・ファイルは一般的にビルドには適しません。同様に、ヒューリスティックに基づくJava VM内部アクセス用の組込みフィルタは、no-builtin-heuristic-filterを使用して無効にできますが、そのようにすると、一般的にはメタデータ・ファイルの有用性が低下します。たとえば、-agentlib:native-image-agent=no-builtin-caller-filter,no-builtin-heuristic-filter,config-output-dir=...のように指定します

アクセス・フィルタ

発生場所に基づいて動的アクセスをフィルタ処理する前述のコール元ベースのフィルタとは異なり、アクセス・フィルタはアクセスのターゲットに適用されます。したがって、アクセス・フィルタを使用すると、生成された構成からパッケージとクラス(およびそのメンバー)を直接除外できます。

デフォルトでは、アクセスされるすべてのクラス(コール元ベースのフィルタおよび組込みフィルタも通過するもの)が生成された構成に追加されます。access-filter-fileオプションを使用すると、前述のファイル構造に従ったカスタム・フィルタ・ファイルを追加できます。このオプションを複数回指定して複数のフィルタ・ファイルを追加し、他のフィルタ・オプションと組み合せることができます。たとえば、-agentlib:access-filter-file=/path/to/access-filter-file,caller-filter-file=/path/to/caller-filter-file,config-output-dir=...のように指定します。

引数としての構成ファイルの指定

構成ファイルを含むディレクトリがクラスパス上にない場合は、native-imageに対して-H:ConfigurationFileDirectories=/path/to/config-dir/を使用して指定できます。このディレクトリには、jni-config.jsonreflect-config.jsonproxy-config.jsonおよびresource-config.jsonのファイルをすべて直接含める必要があります。同じメタデータ・ファイルを含むディレクトリがクラスパス上にあるものの、META-INF/native-image/内にない場合は、-H:ConfigurationResourceRoots=path/to/resources/を使用して指定できます。-H:ConfigurationFileDirectories-H:ConfigurationResourceRootsのいずれでも、カンマで区切ったディレクトリのリストを指定できます。

プロセス環境を介したエージェントの注入

javaコマンドラインを変更してエージェントを注入することは、Javaプロセスがアプリケーションまたはスクリプト・ファイルによって起動される場合、またはJavaが既存のプロセスに埋め込まれている場合は、明らかに困難です。その場合、JAVA_TOOL_OPTIONS環境変数を使用してエージェントを注入することもできます。この環境変数は、同時に実行される複数のJavaプロセスで取得できます。その場合、各エージェントはconfig-output-dirを使用して個別の出力ディレクトリに書き込む必要があります。(次の項では、構成ファイル・セットをマージする方法について説明します。)単一のグローバルJAVA_TOOL_OPTIONS変数で個別のパスを使用するために、エージェントの出力パス・オプションではプレースホルダがサポートされています:

export JAVA_TOOL_OPTIONS="-agentlib:native-image-agent=config-output-dir=/path/to/config-output-dir-{pid}-{datetime}/"

{pid}プレースホルダはプロセス識別子に置き換えられ、{datetime}はUTCでのシステム日時に置き換えられて、ISO 8601に従ってフォーマットされます。前述の例では、結果のパスは/path/to/config-output-dir-31415-20181231T235950Z/のようになります。

トレース・ファイル

前述の例では、native-image-agentを使用して、JVMでの動的アクセスを追跡し、そこから構成ファイル・セットを生成しています。ただし、実行をより深く理解するために、エージェントは個々のアクセスを含むトレース・ファイルをJSON形式で書き込むこともできます:

$JAVA_HOME/bin/java -agentlib:native-image-agent=trace-output=/path/to/trace-file.json ...

native-image-configureツールでは、トレース・ファイルを構成ファイルに変換できます。次のコマンドでは、trace-file.jsonを読み取って処理し、ディレクトリ/path/to/config-dir/に構成ファイル・セットを生成します:

native-image-configure generate --trace-input=/path/to/trace-file.json --output-dir=/path/to/config-dir/

相互運用性

エージェントは、JVM Tool Interface (JVMTI)を使用します。また、JVMTIをサポートする他のJVMとともに使用することもできます。この場合、エージェントの絶対パスを指定する必要があります:

/path/to/some/java -agentpath:/path/to/graalvm/jre/lib/amd64/libnative-image-agent.so=<options> ...

試験段階のオプション

エージェントには、現在試験段階のオプションがあり、将来のリリースで有効になっている可能性がありますが、すべて変更または削除される可能性もあります。ExperimentalAgentOptions.mdガイドを参照してください。

ネイティブ・イメージ構成ツール

前の項で説明したように複数のプロセスでエージェントを同時に使用する場合、config-output-dirは安全なオプションですが、結果として複数の構成ファイル・セットが生成されます。native-image-configureツールを使用すると、これらの構成ファイルをマージできます。このツールは、次のように事前にビルドする必要があります:

native-image --macro:native-image-configure-launcher

その後、このツールを使用して、次のように構成ファイル・セットをマージできます:

native-image-configure generate --input-dir=/path/to/config-dir-0/ --input-dir=/path/to/config-dir-1/ --output-dir=/path/to/merged-config-dir/

このコマンドでは、/path/to/config-dir-0//path/to/config-dir-1/からそれぞれ異なる構成ファイル・セットを読み取り、両方の情報を含む構成ファイル・セットを/path/to/merged-config-dir/に書き込みます。構成ファイル・セットを使用して、任意の数の--input-dir引数を指定できます。すべてのオプションについては、native-image-configure helpを参照してください。

その他の情報