ノート:

GraalVM Native Imageのスタート・ガイド

イントロダクション

このラボでは、GraalVM Native Imageを使用して、クラウド・ネイティブJavaアプリケーションを構築するプロセスを段階的に説明します。Javaの知識を持つ開発者を対象としています。

GraalVM Native Image技術では、Javaコードを時間を前もって自己完結型の実行可能ファイルにコンパイルします。実行時にアプリケーションで必要とされるコードのみが、実行可能ファイルに追加されます。

ネイティブ・イメージによって生成される実行可能ファイルには、次のような重要な利点があります。

業界をリードするマイクロサービス・フレームワークの多くは、Micronaut、Spring、Helidon、Quarkusなど、GraalVM Native Imageのコンパイルを事前にサポートしています。

また、ネイティブ・イメージ用のMavenおよびGradleプラグインがあるため、Javaアプリケーションを実行可能ファイルとして簡単に構築、テストおよび実行することができます。

注意: Oracle Cloud Infrastructure (OCI)は、追加コストなしでGraalVM Enterpriseを提供します。

推定ラボ時間: 45分

ラボの目的

この演習では、次のタスクを実行します。

注:ラボにラップトップアイコンが表示されている場合は、コマンドの入力などを実行する必要があります。その事に注意しなさい。

# This is where we you will need to do something

ステップ1:リモート・ホストへの接続および開発環境の確認

開発環境は、リモート・ホスト(Oracle Linux 8、1 CPUおよび32GBのメモリーを備えたOCIコンピュート・インスタンス)によって提供されます。
Luna Labsデスクトップ環境は、リモート・ホストの準備が完了する前に表示され、最大2分かかる場合があります。

リモート・ホストへの接続は、Lunaデスクトップ環境でセットアップ・スクリプトを実行します。このスクリプトは、「リソース」タブで使用できます

  1. デスクトップで、「Luna-Lab.html」アイコンをダブルクリックします。ページが開き、ラボに固有のOracle Cloud Infrastructure資格証明および情報が表示されます。

  2. 「リソース」タブが表示されます。コンピュート・インスタンスが10分のクラウドでプロビジョニングされている間は、「リソース」タイトルの横に表示された歯車が回転することに注意してください。

  3. インスタンスのプロビジョニング時に最大2分かかる場合、「リソース」タブに次が表示されます

    Lunaリソース・タブ

  4. 「リソース」タブから、VSコード環境を設定する構成スクリプトをコピーします。「詳細の表示」リンクをクリックして、構成を表示します。次のスクリーンショットに示すように、これをコピーします。

    構成スクリプトのコピー

  5. 次のスクリーンショットに示すように、ターミナルを開きます。

    ターミナルを開く

  6. 構成コードを端末に貼り付け、VSコードを開きます。

    端末1を貼り付け

    端末2を貼り付け

完了しました。これで、Oracle Cloudのリモート・ホストに正常に接続されました。

開発環境でのノート

このラボでは、JavaプラットフォームとしてGraalVM Enterprise 21を使用します。GraalVMは、信頼性が高く安全なOracle Java SE上に構築された、Oracleからの高パフォーマンスのJDKディストリビューションです。

この演習に必要なGraalVMとネイティブ・イメージ・ツールがあらかじめ構成されています。

端末で次のコマンドを実行すると、簡単に確認できます。VSコードTerminal > New Terminal内から端末を作成できます。

java -version

native-image --version

ステップ2:デモ・アプリケーションのビルドと実行

デモ・アプリケーションを使用して、GraalVM Native Imageを紹介します。これは、現在のディレクトリとそのサブディレクトリに含まれるファイルの数をカウントするコマンドラインJavaアプリケーションです。さらに、アプリケーションはファイルの合計サイズも計算します。

アプリケーションのソースコードは、リモートホストで使用できます。

デモ・アプリケーションのノート

アプリケーションは、srcディレクトリにある2つのJavaファイルで構成されます。

アプリケーションは、手作業で構築することも、Mavenプロファイルを使用して構築することもできます。Mavenビルド構成は、pom.xmlファイルで提供されます。Mavenプロファイルは、1つのpom.xmlファイル内に異なるビルド構成を持つ優れた方法です。Mavenプロファイルの詳細は、ここで確認できます。

開いているVSコード内のファイルを参照できます。

この演習では、いくつかのプロファイルを使用します。各プロファイルには特定の目的があります。

  1. native:このプロファイルは、GraalVM Native Imageを使用して実行可能ファイルを作成します。
  2. java_agent:このプロファイルは、アプリケーション内の動的コードのすべての使用状況を追跡し、この情報を構成ファイルに取得するトレース・エージェントを使用してJavaアプリケーションを作成します。詳細については後述します。

特定のMavenプロファイルは、パラメータとしてmvnコマンドに渡すことによって使用します。プロファイルの名前は、-Pフラグに追加されます。VSコード内のターミナル内から次のコマンドを実行できます。

次の例は、Mavenを使用して構築するときにnativeプロファイルをコールする方法を示しています:

mvn clean package -Pnative

これで、アプリケーションの動作を基本的に理解して、その動作を確認します。

  1. プロジェクトを構築し、VS Codeで開かれたターミナル内から実行します。

    mvn clean package exec:exec
    

    前述のコマンドは次のとおりです:

    1. 生成されたアーティファクトまたはコンパイル済のアーティファクトを削除するには、プロジェクトをクリアします。
    2. アプリケーションを含む実行可能なJARファイルを作成します。このJARファイルは、後でネイティブ・イメージで使用されます。
    3. execプラグインを実行してアプリケーションを実行します。

    生成された出力には次のものが表示されます(アプリケーションによって報告されるファイルの数は異なる場合があります)。

    Counting directory: .
    Total: 15 files, total size = 511.9 KiB
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    

ステップ3:実行可能ファイルへのJavaアプリケーションの変更

次に、GraalVM Native Imageを使用して、アプリケーションの実行可能バージョンを作成します。簡潔なリマインダとして、GraalVM Native Imageは、Javaアプリケーションを、JDKの実行を必要としない自己完結型の実行ファイルに変換する、すぐに開始して効率化できるコンパイル・テクノロジです。

GraalVM Native Imageは、リモート・ホストに事前インストールされています。

  1. 最初に、targetディレクトリにコンパイル済JARファイルがあることを確認します。

    ls ./target
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 archive-tmp
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 classes
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 generated-sources
    -rw-rw-r-- 1 krf krf  496273 Mar  4 11:38 graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar
    -rw-rw-r-- 1 krf krf    7894 Mar  4 11:38 graalvmnidemos-1.0-SNAPSHOT.jar
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 maven-archiver
    drwxrwxr-x 1 krf krf    4096 Mar  4 11:12 maven-status
    

    必要なファイルはgraalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jarです。

  2. コマンドラインから実行可能ファイルを生成します。Mavenプラグインを使用してGraalVM Native Imageを使用する必要はありませんが、これは役立ちます。プロジェクトのルート・ディレクトリdemoから次を実行します。

    native-image -jar ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar --no-fallback -H:Class=oracle.App -H:Name=file-count
    

    これにより、現在のディレクトリ内にfile-countという実行可能ファイルが生成されます。

  3. この実行可能ファイルを次のように実行します。

    ./file-count
    
  4. アプリケーションを実行する時刻。最初に実行可能ファイルとして実行し、次に通常のjavaコマンドを使用します。

    time ./file-count
    

    time java -cp ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar oracle.App
    

    native-imageコマンドによって生成された実行可能ファイルは、対応するJavaアプリケーションよりも大幅に高速で実行されます。

実行可能ファイルの作成方法を少し詳しく説明します。

ステップ2でnative-imageコマンドに渡したパラメータは何を指定しますか。

完全なドキュメントはここにあります。

GraalVM Native Image Mavenプラグインを使用して、native-imageツールを実行することもできます。プロジェクトpom.xml (Maven構成)ファイルには、プラグインを使用して実行可能ファイルを構築する方法を示す次のスニペットが含まれています。

<!-- Native Image -->
<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
    <version>${native.maven.plugin.version}</version>
    <extensions>true</extensions>
    <executions>
    <execution>
        <id>build-native</id>
        <goals>
        <goal>build</goal>
        </goals>
        <phase>package</phase>
    </execution>
    </executions>
    <configuration>
        <skip>false</skip>
        <imageName>${exe.file.name}</imageName>
        <mainClass>${app.main.class}</mainClass>
        <buildArgs>
            <buildArg>--no-fallback</buildArg>
            <buildArg>--report-unsupported-elements-at-runtime</buildArg>
        </buildArgs>
    </configuration>
</plugin>

ネイティブ・イメージMavenプラグインは、実行可能ファイルを作成する重いリフトを実行します。<skip>true</skip>タグを使用して無効にできます。また、<buildArgs/>タグを介してパラメータをnative-imageに渡すこともできます。

GraalVM Native Imageプラグインについては、こちらをご覧ください。

Mavenプロファイルを使用して実行可能ファイルをビルドするには、次を実行します:

mvn clean package -Pnative

Mavenビルドによって、実行可能ファイルfile-counttargetディレクトリに配置されます。

実行可能ファイルは、次のように実行できます。

./target/file-count

ステップ4:リフレクションの使用- Log4Jへの依存性の追加

このステップでは、Javaの動的機能と連携する実行可能ファイルを構築します。

リフレクションに依存するアプリケーションにライブラリまたはコードを追加するとします。リフレクションをテストする適切な候補は、Log4Jロギング・フレームワークです。プロジェクトpom.xmlファイルの依存性としてすでに追加されています。

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

log4jを使用するようにアプリケーションを変更するには、ListDir.javaファイルを編集し、いくつかの行をコメント解除します。

  1. VSコードを使用して、ListDir.javaファイルを開きます

  2. log4jインポートを宣言する行のコメントを解除し、次の行のコメントを解除します。

    //import org.apache.log4j.Logger;
    

    //final static Logger logger = Logger.getLogger(ListDir.class);
    

    /*
    // Add some logging
    if(logger.isDebugEnabled()){
        logger.debug("Processing : " + dirName);
    }
    */
    

    /*
    // Add some logging
    if(logger.isDebugEnabled()){
        logger.debug("Processing : " + f.getAbsolutePath());
    }
    */
    
  3. ファイルを保存します

    アプリケーションにロギングを追加したので、再構築して実行することにより、変更の結果を表示できます。

    mvn clean package exec:exec
    

    前の手順と同じ種類の出力が表示されますが、さらにロギングが追加されています。

  4. 次に、Mavenプロファイルを使用して実行可能ファイルを作成します。

    mvn clean package -Pnative
    
  5. 作成した実行可能ファイルを実行します。これには、ロギングが含まれます。

    ./target/file-count
    

    これにより、次のエラーが生成されます。

    Exception in thread "main" java.lang.NoClassDefFoundError
            at org.apache.log4j.Category.class$(Category.java:118)
            at org.apache.log4j.Category.<clinit>(Category.java:118)
            at java.lang.Class.ensureInitialized(DynamicHub.java:552)
            at oracle.ListDir.<clinit>(ListDir.java:75)
            at oracle.App.main(App.java:63)
    Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Category
            at java.lang.Class.forName(DynamicHub.java:1433)
            at java.lang.Class.forName(DynamicHub.java:1408)
            ... 5 more
    

    ここで何が起こったのですか。

Javaの動的機能の操作

この例外は、リフレクションに依存するため、Log4Jライブラリの追加によって発生します。
native-imageツールは、積極的な静的分析を実行して、アプリケーション内でどのクラスが使用されているかを確認します。使用されていないクラスの場合、ツールはそれらが不要であると想定します。これは、「クローズされた世界」仮定と呼ばれます。ロードする必要があるものは、実行可能ファイルの作成時に知っておく必要があります。静的分析で検索できない場合、実行可能ファイルには含まれません。

反射はJavaのコア機能なので、反射は使用し、GraalVM Native Imageで提供されるスピードアップをどのように利用できますか。リフレクションの使用についてnative-imageツールに知らせる方法が必要です。

native-imageツールは、リフレクションを介して参照されるすべてのクラスを指定する構成ファイルで読み取ることができます。

これは手動で実行することも、GraalVM Javaランタイムに付属のJavaトレース・エージェントでも実行できます。エージェントによってJSONファイルが生成され、リフレクション、JNI、プロキシおよびリソース・アクセスのすべてのインスタンスを記録し、アプリケーションの実行中に特定できます。

ノート:リフレクションのすべてのケースが確実に識別されるように、トレース・エージェントの実行時にアプリケーションのすべてのコード・パスを実行することが重要です。

トレース・エージェントの詳細なドキュメントはここにあります。

ステップ5:トレース・エージェントの使用

ここで、アプリケーションの実行中に、トレース・エージェントを使用してリフレクション構成を生成します。

  1. トレース・エージェントを使用してアプリケーションを実行します。

    java -agentlib:native-image-agent=config-output-dir=./src/main/resources/META-INF/native-image -cp ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar oracle.App
    

    トレース・エージェントが作成した構成ファイルを確認します。

    ls -l src/main/resources/META-INF/native-image/
    

    生成された出力には、次のものが表示されます。

    total 56
    -rw-r--r--  1 kfoster  staff     4B Dec  2 19:13 jni-config.json
    -rw-r--r--  1 kfoster  staff    86B Nov  9 20:46 native-image.properties
    -rw-r--r--  1 kfoster  staff    65B Dec  2 19:13 predefined-classes-config.json
    -rw-r--r--  1 kfoster  staff     4B Dec  2 19:13 proxy-config.json
    -rw-r--r--  1 kfoster  staff   521B Dec  2 19:13 reflect-config.json
    -rw-r--r--  1 kfoster  staff   101B Dec  2 19:13 resource-config.json
    -rw-r--r--  1 kfoster  staff     4B Dec  2 19:13 serialization-config.json
    

    ノート:プロジェクトには、これを実行できるMavenプロファイルが含まれています。トレース・エージェントを使用するには、次のコマンドを実行します。

    mvn clean package exec:exec -Pjava_agent
    
  2. 次に、実行可能ファイルを再度構築します。今回は、トレース・エージェントによって生成された構成ファイルが適用されます。

    mvn package -Pnative
    
  3. 最後に、生成されたファイルを実行します。

    time ./target/file-count
    

    実行可能ファイルが機能し、予期したとおりに出力へのログ・メッセージが生成されます。

これは、トレース・エージェントによって生成されたファイルが、リフレクションによって参照されるクラスを記録しているために機能します。native-imageツールでは、アプリケーション内で使用されているため、生成された実行可能ファイルから除外されないことが認識されるようになりました。

-agentlibパラメータの位置に注意してください

エージェント・パラメータは、-jarまたは-classpathパラメータの前になる必要があります。また、ファイルを書き込むディレクトリも指定する必要があります。推奨される場所はsrc/main/resources/META-INF/native-imageにあります。この場所に配置されたファイルは、native-imageツールによって自動的に取得されます。

実行可能ファイルの生成の構成に関するノート

Javaプロパティ・ファイル(デフォルトではsrc/main/resources/META-INF/native-image/native-image.properties)を使用して、native-imageツールにパラメータを渡すこともできます。demoディレクトリにサンプル・ファイルがあり、これを使用して実行できることを把握できます。

結論

このラボでは、GraalVM Native Imageのいくつかの機能を試してみました。

  1. Javaコマンドライン・アプリケーションから高速実行可能ファイルを生成する方法
  2. Mavenを使用して実行可能ファイルを構築する方法
  3. トレースエージェントを使用してリフレクションを追跡および記録するプロセスを自動化する方法

GraalVM Native Imageを使用した効率的で安全かつ即時利用可能なクラウド・ネイティブJavaアプリケーションの記述

さらに学ぶ

その他の学習リソース

docs.oracle.com/learnの他のラボを調べるか、Oracle Learning YouTubeチャネルでさらに無料の学習コンテンツにアクセスします。さらに、education.oracle.com/learning-explorerにアクセスしてOracle Learning Explorerにします。

製品ドキュメントは、Oracleヘルプ・センターを参照してください。