graalvm-native-image について
Java 開発者向けに、GraalVM Native Image の実行ファイル作成をビルド設定からメタデータの問題解決まで案内するスキルです。Maven、Gradle に加え、Spring Boot、Quarkus、Micronaut の設定も扱います。
主な機能
- 01Maven と Gradle の設定ガイド
- 02フレームワーク別の Native 設定
- 03到達可能性メタデータの管理
- 04反復的なネイティブビルドの問題解決
- 05実行ファイルの検証と測定
- 06Docker イメージのガイド
活用シーン
- 01JVM アプリケーションのネイティブ実行ファイル化
- 02デプロイ時のコールドスタート短縮
- 03リフレクションやリソース関連のビルドエラー解決
- 04ネイティブ実行時の起動時間とメモリ使用量の測定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill graalvm-native-imageインストール後、Claude Code で/graalvm-native-imageを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/gradle-native-plugin.mdmaven-native-profile.mdquarkus-micronaut-native.mdreflection-resource-config.mdspring-boot-native.mdtracing-agent.md詳細
Java アプリケーション向け GraalVM Native Image
GraalVM Native Image を使用して Java アプリケーションから高性能なネイティブ実行ファイルを構築するための専門スキルです。起動時間とメモリ消費量を大幅に削減できます。
概要
GraalVM Native Image は、Java アプリケーションを事前コンパイル(AOT)して、スタンドアロンのネイティブ実行ファイルを生成します。これらの実行ファイルは数ミリ秒で起動し、JVM ベースのデプロイと比べて必要なメモリが大幅に少なくなります。起動の速さとリソース使用量の少なさが重要となるサーバーレス関数、CLI ツール、マイクロサービスに最適です。
このスキルでは、JVM アプリケーションをネイティブバイナリに移行するための体系的なワークフローを提供します。ビルドツールの設定、フレームワーク固有のパターン、リフレクションメタデータの管理、ネイティブビルドの失敗を反復的に解決する手法を扱います。
使用するタイミング
次の場合にこのスキルを使用します。
- JVM ベースの Java アプリケーションを GraalVM のネイティブ実行ファイルに変換する
- サーバーレスまたはコンテナ化されたデプロイのコールドスタート時間を最適化する
- Java マイクロサービスのメモリ使用量(RSS)を削減する
- GraalVM Native Build Tools を使用するよう Maven または Gradle を設定する
- ネイティブビルドでの
ClassNotFoundException、NoSuchMethodException、またはリソース不足のエラーを解決する reflect-config.json、resource-config.json、またはその他の GraalVM メタデータファイルを生成または編集する- GraalVM トレーシングエージェントを使用してリーチャビリティメタデータを収集する
- Spring Boot のネイティブサポート向けに
RuntimeHintsを実装する - Quarkus または Micronaut でネイティブイメージをビルドする
手順
1. プロジェクトの状況分析
設定を行う前に、プロジェクトを分析してビルドツール、フレームワーク、依存関係を特定します。
ビルドツールを検出する:
# Check for Maven
if [ -f "pom.xml" ]; then
echo "Build tool: Maven"
# Check for Maven wrapper
[ -f "mvnw" ] && echo "Maven wrapper available"
fi
# Check for Gradle
if [ -f "build.gradle" ] || [ -f "build.gradle.kts" ]; then
echo "Build tool: Gradle"
[ -f "build.gradle.kts" ] && echo "Kotlin DSL"
[ -f "gradlew" ] && echo "Gradle wrapper available"
fi
依存関係を分析してフレームワークを検出する:
- Spring Boot:
pom.xmlまたはbuild.gradleでspring-boot-starter-*を探します - Quarkus:
quarkus-*依存関係を探します - Micronaut:
micronaut-*依存関係を探します - プレーンな Java:フレームワークの依存関係が見つからない状態です
Java のバージョンを確認する:
java -version 2>&1
# GraalVM Native Image requires Java 17+ (recommended: Java 21+)
ネイティブイメージで起こりうる問題を特定する:
- リフレクションを多用するライブラリ(Jackson、Hibernate、JAXB)
- 動的プロキシの使用(JDK プロキシ、CGLIB)
- リソースバンドルとクラスパスリソース
- JNI またはネイティブライブラリへの依存関係
- シリアライズの要件
2. ビルドツールの設定
検出した環境に応じて、適切なビルドツールプラグインを設定します。
Maven プロジェクトの場合、標準ビルドをシンプルに保つため、専用の native プロファイルを追加します。設定の詳細は、Maven ネイティブプロファイルのリファレンスを参照してください。
Maven の主な設定:
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.10.6</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<imageName>${project.artifactId}</imageName>
<buildArgs>
<buildArg>--no-fallback</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
次のコマンドでビルドします:./mvnw -Pnative package
Gradle プロジェクトの場合、org.graalvm.buildtools.native プラグインを適用します。設定の詳細は、Gradle ネイティブプラグインのリファレンスを参照してください。
Gradle の主な設定(Kotlin DSL):
plugins {
id("org.graalvm.buildtools.native") version "0.10.6"
}
graalvmNative {
binaries {
named("main") {
imageName.set(project.name)
buildArgs.add("--no-fallback")
}
}
}
次のコマンドでビルドします:./gradlew nativeCompile
3. フレームワーク固有の設定
各フレームワークには、それぞれ独自の AOT 戦略があります。検出したフレームワークに応じて適切な設定を適用します。
Spring Boot(3.x 以降):Spring Boot には AOT 処理を含む、GraalVM のサポートが組み込まれています。RuntimeHints、@RegisterReflectionForBinding、テストサポートなどのパターンについては、Spring Boot ネイティブのリファレンスを参照してください。
主なポイント:
- ネイティブプロファイルが含まれる
spring-boot-starter-parent3.x 以降を使用します RuntimeHintsRegistrarを使用してリフレクションヒントを登録しますprocess-aotゴールで AOT 処理を実行します- 次のコマンドでビルドします:
./mvnw -Pnative native:compileまたは./gradlew nativeCompile
Quarkus と Micronaut:これらのフレームワークはネイティブを前提に設計されているため、追加の設定は最小限で済みます。Quarkus と Micronaut のリファレンスを参照してください。
4. GraalVM の到達可能性メタデータ
Native Image はクローズドワールドの前提を使用します。つまり、すべてのコードパスをビルド時に把握しておく必要があります。リフレクション、リソース、プロキシなどの動的機能には、明示的なメタデータ設定が必要です。
メタデータファイルは、META-INF/native-image/<group.id>/<artifact.id>/ に配置します。
| ファイル | 用途 |
|---|---|
reachability-metadata.json | 統合メタデータ(リフレクション、リソース、JNI、プロキシ、バンドル、シリアライゼーション) |
reflect-config.json | レガシー形式:リフレクション登録 |
resource-config.json | レガシー形式:リソースの包含パターン |
proxy-config.json | レガシー形式:動的プロキシインターフェース |
serialization-config.json | レガシー形式:シリアライゼーション登録 |
jni-config.json | レガシー形式:JNI アクセス登録 |
形式と例について詳しくは、リフレクションとリソースの設定リファレンスを参照してください。
5. 反復的な修正エンジン
Native Image のビルドは、メタデータの不足が原因で失敗することがよくあります。次の反復的な手順に従ってください。
ステップ 1 — ネイティブビルドを実行します。
# Maven
./mvnw -Pnative package 2>&1 | tee native-build.log
# Gradle
./gradlew nativeCompile 2>&1 | tee native-build.log
ステップ 2 — ビルドエラーを解析し、根本原因を特定します。
よくあるエラーパターンと修正方法は次のとおりです。
| エラーパターン | 原因 | 修正方法 |
|---|---|---|
ClassNotFoundException: com.example.MyClass | リフレクションメタデータの不足 | reflect-config.json に追加するか、@RegisterReflectionForBinding を使用します |
NoSuchMethodException | メソッドがリフレクションに登録されていない | リフレクション設定にメソッドを追加します |
MissingResourceException | リソースが Native Image に含まれていない | resource-config.json に追加します |
Proxy class not found | 動的プロキシが登録されていない | インターフェースのリストを proxy-config.json に追加します |
UnsupportedFeatureException: Serialization | シリアライゼーションメタデータの不足 | serialization-config.json に追加します |
ステップ 3 — 修正を適用します。 適切なメタデータファイルを更新するか、フレームワークのアノテーションを使用します。
ステップ 4 — 再ビルドして検証します。 ビルドが成功するまで繰り返します。
ステップ 5 — 手動での修正が不十分な場合は、GraalVM トレーシングエージェントを使って到達可能性メタデータを自動的に収集します。トレーシングエージェントのリファレンスを参照してください。
6. 検証とベンチマーク
ネイティブビルドが成功したら、次の手順を実行します。
実行ファイルが正しく動作することを確認します。
# Run the native executable
./target/<app-name>
# For Spring Boot, verify the application context loads
curl http://localhost:8080/actuator/health
起動時間を測定します。
# Time the startup
time ./target/<app-name>
# For Spring Boot, check the startup log
./target/<app-name> 2>&1 | grep "Started .* in"
メモリ使用量(RSS)を測定します。
# On Linux
ps -o rss,vsz,comm -p $(pgrep <app-name>)
# On macOS
ps -o rss,vsz,comm -p $(pgrep <app-name>)
JVM のベースラインと比較します。
| 指標 | JVM | Native | 改善 |
|---|---|---|---|
| 起動時間 | ~2-5s | ~50-200ms | 10-100x |
| メモリ(RSS) | ~200-500MB | ~30-80MB | 3-10x |
| バイナリサイズ | JRE + JARs | 単一バイナリ | 簡素化 |
7. Docker との統合
ネイティブ実行ファイルを使って、最小限のコンテナーイメージをビルドします。
# Multi-stage build
FROM ghcr.io/graalvm/native-image-community:21 AS builder
WORKDIR /app
COPY . .
RUN ./mvnw -Pnative package -DskipTests
# Minimal runtime image
FROM debian:bookworm-slim
COPY --from=builder /app/target/<app-name> /app/<app-name>
EXPOSE 8080
ENTRYPOINT ["/app/<app-name>"]
Spring Boot アプリケーションでは、Cloud Native Buildpacks と paketobuildpacks/builder-jammy-tiny を使用します。
./mvnw -Pnative spring-boot:build-image
ベストプラクティス
- 複雑なプロジェクトではトレーシングエージェントから始めて、メタデータの初期ベースラインを生成します
nativeプロファイルを使用して、ネイティブ固有の設定を標準ビルドと分離します--no-fallbackを優先して使用し、真のネイティブビルド(JVM へのフォールバックなし)を確保しますnativeTestを使用して、ネイティブモードで JUnit テストを実行します- GraalVM Reachability Metadata Repository を使用して、サードパーティライブラリのメタデータを取得します
- リフレクションを最小限に抑えます。 可能な場合は、コンストラクターインジェクションとコンパイル時 DI を優先します
- クラスパスのスキャンに頼らず、リソースパターンを明示的に指定します
- 実行前後にプロファイリングします。 起動時間とメモリの改善を必ず測定します
- GraalVM との互換性とパフォーマンスを最大限に高めるには、Java 21 以降を使用します
- GraalVM と Native Build Tools のバージョンを揃えます
例
例 1:Spring Boot Maven プロジェクトにネイティブサポートを追加する
シナリオ: Spring Boot 3.x の REST API を、ネイティブ実行ファイルにコンパイルしたいとします。
ステップ 1 — ネイティブプロファイルを pom.xml に追加します:
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>process-aot</id>
<goals>
<goal>process-aot</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
ステップ 2 — DTO のリフレクションヒントを登録します:
@RestController
@RegisterReflectionForBinding({UserDto.class, OrderDto.class})
public class UserController {
@GetMapping("/users/{id}")
public UserDto getUser(@PathVariable Long id) {
return userService.findById(id);
}
}
ステップ 3 — ビルドして実行します:
./mvnw -Pnative native:compile
./target/myapp
# Started MyApplication in 0.089 seconds
例 2:ネイティブビルドで発生するリフレクションエラーを解決する
シナリオ: Jackson でシリアライズされた DTO に対して ClassNotFoundException が発生し、ネイティブビルドが失敗します。
エラー出力:
com.oracle.svm.core.jdk.UnsupportedFeatureError:
Reflection registration missing for class com.example.dto.PaymentResponse
修正 — src/main/resources/META-INF/native-image/reachability-metadata.json に追加します:
{
"reflection": [
{
"type": "com.example.dto.PaymentResponse",
"allDeclaredConstructors": true,
"allDeclaredMethods": true,
"allDeclaredFields": true
}
]
}
または、Spring Boot のアノテーションを使用します:
@RegisterReflectionForBinding(PaymentResponse.class)
@Service
public class PaymentService { /* ... */ }
例 3:複雑なプロジェクトでトレーシングエージェントを使用する
シナリオ: 多数のサードパーティライブラリを含むプロジェクトで、包括的な到達可能性メタデータが必要です。
# 1. Build the JAR
./mvnw package -DskipTests
# 2. Run with the tracing agent
java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image \
-jar target/myapp.jar
# 3. Exercise all endpoints
curl http://localhost:8080/api/users
curl -X POST http://localhost:8080/api/orders -H 'Content-Type: application/json' -d '{"item":"test"}'
curl http://localhost:8080/actuator/health
# 4. Stop the application (Ctrl+C), then build native
./mvnw -Pnative native:compile
# 5. Verify
./target/myapp
制約と警告
重大な制約
- GraalVM Native Image には Java 17 以降が必要です(最良の互換性を得るには Java 21 以降を推奨します)
- 閉じた世界の前提: すべてのコードパスがビルド時に把握されている必要があります。動的クラス読み込み、実行時のバイトコード生成、
MethodHandles.Lookupは動作しない場合があります - ビルド時間とメモリ: ネイティブコンパイルはリソースを大量に消費します。一般的なプロジェクトでは 2~10 分、4~8 GB の RAM が必要です
- すべてのライブラリに互換性があるわけではありません: リフレクション、動的プロキシ、CGLIB に大きく依存するライブラリでは、広範なメタデータ設定が必要になる場合があります
- AOT プロファイルはビルド時に固定されます: Spring Boot の
@Profileと@ConditionalOnPropertyは、実行時ではなく AOT 処理中に評価されます
よくある落とし穴
--no-fallbackを指定し忘れる: このフラグがないと、真のネイティブ実行ファイルではなく、JVM フォールバックイメージがビルドによって気付かないうちに生成される場合があります- トレーシングエージェントのカバレッジが不完全: エージェントが取得するのは実行中に通ったコードパスのみです。すべての機能をテストしてください
- バージョンの不一致: 互換性の問題を避けるため、GraalVM JDK、Native Build Tools プラグイン、フレームワークのバージョンを揃えてください
- クラスパスの違い: AOT/ビルド時のクラスパスは実行時と一致している必要があります。ネイティブコンパイル後に JAR を追加または削除すると、失敗の原因になります
セキュリティに関する考慮事項
- ネイティブ実行ファイルは JAR より逆コンパイルが困難ですが、改ざんを防げるわけではありません
- ビルド時にシークレットがネイティブイメージに埋め込まれないようにしてください
- 機密データには環境変数または外部設定を使用してください
トラブルシューティング
| 問題 | 解決策 |
|---|---|
| ビルド中にメモリが不足する | buildArgs に -J-Xmx8g を指定してビルドメモリを増やします |
| ビルドに時間がかかりすぎる | ビルドキャッシュを使用し、クラスパスを縮小して、開発用にクイックビルドモードを有効にします |
| アプリケーションが実行時にクラッシュする | リフレクション/リソースのメタデータが不足しています。トレーシングエージェントを実行してください |
| Spring Boot のコンテキストの読み込みに失敗する | @Conditional ビーンとプロファイル依存の設定を確認します |
| サードパーティライブラリに互換性がない | GraalVM Reachability Metadata リポジトリを確認するか、手動でヒントを追加します |