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

Native Imageツールは、実行時にアプリケーションの到達可能なコードの静的分析に依存します。しかし、この分析では、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/ ...                                                              ^^^^^

エージェントは、メタデータを定期的に書き込むための以下のオプションも提供します。

  • config-write-period-secs=n:メタデータファイルをn秒ごとに書き込みます。 nは0より大きい値でなければなりません。
  • config-write-initial-delay-secs=n:メタデータを最初に書き込む前にn秒待機します。デフォルトは1です。

例えば

$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ツールに提供できます。このディレクトリ(またはそのサブディレクトリ)は、*reachability-metadata.json*という名前のファイルが検索され、ビルドプロセスに自動的に含まれます。これらのファイルすべてが存在する必要はありません。同じ名前のファイルが複数見つかった場合は、すべてが考慮されます。

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

条件付きメタデータ収集 #

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

エージェントで条件付きメタデータを収集するには、条件付きメタデータ収集を参照してください。

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

呼び出し元ベースのフィルタ #

デフォルトでは、エージェントは、Native Imageが設定なしでサポートする動的アクセスをフィルタリングします。フィルタメカニズムは、アクセスを実行するJavaメソッド(*呼び出し元*メソッドとも呼ばれます)を識別し、その宣言クラスをフィルタルールのシーケンスと照合することによって機能します。組み込みのフィルタルールは、JVM、またはNative Imageによって直接サポートされる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セクションのみがクラスを含めるか除外するかを決定します。

テスト目的で、Javaクラスライブラリルックアップの組み込みフィルタは、no-builtin-caller-filterオプションを追加することで無効にできますが、結果のメタデータファイルは、一般にビルドには適していません。同様に、ヒューリスティックに基づく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=....

引数として設定ファイルを指定する #

クラスパスの一部ではない設定ファイルを含むディレクトリは、-H:ConfigurationFileDirectories=/path/to/config-dir/を介してnative-imageに指定できます。このディレクトリには、*reachability-metadata.json*または以前使用されていた個々のメタデータファイル(*jni-config.json*、*reflect-config.json*、*proxy-config.json*、*serialization-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} はISO 8601に従ってフォーマットされたUTCのシステム日時で置き換えられます。上記の例では、結果のパスは /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 ガイドを参照してください。

Native Image Configureツール #

前のセクションで説明したように、複数のプロセスで同時にエージェントを使用する場合、config-output-dir は安全なオプションですが、複数の設定ファイルセットが生成されます。native-image-configure ツールを使用して、これらの設定ファイルをマージできます。

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/ から1つの設定ファイルセットを読み取り、/path/to/config-dir-1/ から別の設定ファイルセットを読み取り、両方の情報を含む設定ファイルセットを /path/to/merged-config-dir/ に書き込みます。設定ファイルセットを含む任意の数の --input-dir 引数を指定できます。すべてのオプションについては、native-image-configure help を参照してください。

参考資料 #

お問い合わせ