- Spring
Spring BootのGraalVMネイティブイメージ|作り方と制約
ネイティブイメージは、Java のアプリをビルドの時点で機械語の実行ファイルにする方法です。 起動が速く、メモリも少なくてすみます。そのかわり、ビルドに数分と1GB 以上のメモリがかかり、「実行時にクラスやプロファイルを切り替える」ような動的な使い方に制約が付きます。Spring Boot 4.1.1 では、Gradle なら org.graalvm.buildtools.native プラグインを足して gradle nativeCompile、Maven なら mvn -Pnative native:compile で作れます。
GraalVM CE 25.0.2 で、Spring を使わない小さな HTTP サーバー(JDK の HttpServer)を JVM とネイティブで動かして比べた結果です(5回の中央値)。
| 項目 | JVM(OpenJDK 25.0.4) | ネイティブイメージ |
|---|---|---|
| 起動から最初の応答まで | 333ms | 10ms |
| 最初の応答のあとのメモリ(RSS) | 56.7MB | 17.5MB |
| 200回応答したあとのメモリ(RSS) | 70.3MB | 44.0MB |
| 配るもの | jar(数 KB)+ JDK | 実行ファイル 1 つ(15.93MB) |
| ビルド時間 | 数秒(javac) | 1分37秒〜2分49秒 |
| ビルド中の最大メモリ | — | 1.17〜1.18GB |
確認環境:GraalVM CE 25.0.2(native-image 25.0.2、Linux x64、2 CPU・メモリ 7GB)で、上の小さなプログラムを実際にビルドして、起動時間・メモリ(/proc の VmRSS)・ビルド時間・リフレクションのエラーと直し方を確かめました。JVM 側は OpenJDK 25.0.4.1 と、GraalVM 付属の JVM で動かしています。Spring Boot の手順・制約・AOT の生成物は Spring Boot 4.1.1 のリファレンスとソース(v4.1.1 のタグ)で、RuntimeHints の API は Spring Framework v7.0.9 のソースで確かめました(確認日 2026年10月13日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Spring BootのGraalVMネイティブイメージ|作り方と制約」
向いている・向いていない
| 使いどころ | 向き | 理由 |
|---|---|---|
| サーバーレス(AWS Lambda など)・すぐ止めるジョブ | ◎ | 起動の速さがそのまま待ち時間・料金に効く |
| 台数をすばやく増減するコンテナ | ○ | 起動が速く、1台あたりのメモリが少ない |
| コマンドラインのツール | ◎ | JDK を入れずに1ファイルで配れる |
| ずっと動かす高負荷の API | △ | JVM は動きながら最適化(JIT)するので、長く動かすと速くなる |
| 実行時にプラグイン・クラスを差し替える | × | クラスパスはビルド時に固定される |
| 開発中に何度もビルドし直す | × | 1回のビルドに数分かかる |
Lambda の起動を速くする方法の比較は、関連記事「Lambdaコールドスタート対策|SnapStartとプロビジョンド同時実行の比較」で扱っています。JVM のままで起動を速くする方法(AOT キャッシュ)は、関連記事「Spring Boot Dockerfile|コピペ用全文とAOTキャッシュ設定」で扱っています。
何が起きているか:AOT と「閉じた世界」
Spring Boot のリファレンス(4.1.1)では、JVM との違いとして次のものが挙げられています。
- ビルドの時点で、
mainからたどれるコードだけを調べ、たどれないコードは実行ファイルに入らない - GraalVM はコードの動的な部分(リフレクション・リソース・シリアライズ・動的プロキシ)を自分では見つけられず、教える必要がある
- クラスパスはビルド時に固定され、変えられない
- 遅延読み込みは無く、実行ファイルに入ったものは起動時にメモリに読まれる
これを「閉じた世界(closed-world)」の前提と呼びます。Spring は、この前提に合わせるために、ビルド時に AOT 処理(Ahead-of-Time 処理、実行前の下ごしらえ)を行います。
| AOT 処理で作られるもの | 中身 | 置き場所(Gradle) |
|---|---|---|
| Java のソース | @Configuration を読んで作った Bean 定義(MyConfiguration__BeanDefinitions など) |
build/generated/aotSources |
| バイトコード | CGLIB のプロキシなど | build/generated/aotClasses |
| GraalVM 用のヒント(JSON) | リフレクション・リソース・シリアライズ・プロキシ・JNI | META-INF/native-image/{groupId}/{artifactId}/ |
Maven の場合は target/spring-aot/main/sources・target/spring-aot/main/classes です。実行時に @Configuration を読んで Bean を組み立てる処理を、ビルド時に済ませておくので、起動が速くなります。
作り方
前提
| 必要なもの | 内容 |
|---|---|
| GraalVM(native-image 付き) | JDK 25 版。Boot のドキュメントでは Liberica Native Image Kit 25 が例に挙がっている |
| C コンパイラ | Linux は gcc と zlib の開発用ファイル。Windows は Visual Studio Build Tools と Windows SDK |
| メモリ | 手元のビルドでは最大 1.18GB 使った(下の実測) |
| Buildpacks で作る場合 | Docker と JDK 25 以上。ローカルの GraalVM は不要 |
native-image はクロスコンパイルができません(Boot のドキュメント)。Linux 用の実行ファイルは Linux で、macOS 用は macOS でビルドします。
Gradle
// build.gradle.kts
plugins {
java
id("org.springframework.boot") version "4.1.1"
id("io.spring.dependency-management") version "1.1.7"
id("org.graalvm.buildtools.native") version "1.1.8"
}
java {
toolchain { languageVersion = JavaLanguageVersion.of(25) }
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-webmvc")
testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test")
}
./gradlew nativeCompile # 実行ファイルを作る
./build/native/nativeCompile/myproject # 動かす
./gradlew nativeTest # テストをネイティブで動かす
./gradlew bootBuildImage # Buildpacks でコンテナイメージにする(Docker が必要)
1.1.8 は、Spring Boot v4.1.1 のソース(gradle.properties の nativeBuildToolsVersion)で使われている Native Build Tools の版です。org.graalvm.buildtools.native プラグインを入れると、Boot の Gradle プラグインが AOT 処理のタスク(processAot)を自動で組み込みます。
Maven
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.1</version>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
mvn -Pnative native:compile # target/myproject ができる
mvn -PnativeTest test # テストをネイティブで動かす
mvn -Pnative spring-boot:build-image # Buildpacks でコンテナイメージにする
spring-boot-starter-parent の native プロファイルは、ドキュメントでは「process-aot の実行」「Native Build Tools の既定値(実行可能 jar ではなくクラスパスを使う、GraalVM の版の確認、サードパーティの到達可能性メタデータのダウンロード)」をまとめて設定します。Maven の基本は、関連記事「Mavenコマンド一覧|pom.xml書き方とよく使うオプション」で扱っています。
ネイティブにする前に JVM で確かめる
ビルドに数分かかるので、AOT で作ったコードを JVM で先に動かして、Bean の構成が正しいかを確かめると早く進みます。
mvn -Pnative package
java -Dspring.aot.enabled=true -jar target/myproject-0.0.1-SNAPSHOT.jar
Boot のドキュメントでは、spring.aot.enabled=true で起動できれば「ネイティブにしても動く見込みが高くなる」と説明されています。ログには Starting AOT-processed MyApplication と出ます。
実測:ビルドの時間とメモリ
GraalVM CE 25.0.2 で、Spring を使わない小さなプログラム(JDK の HttpServer で JSON を返す、クラス2つ)を native-image -jar app.jar でビルドしたログの要点です。
[1/8] Initializing... (10.2s @ 0.11GB)
Java version: 25.0.2+10, vendor version: GraalVM CE 25.0.2+10.1
Graal compiler: optimization level: 2, target machine: x86-64-v3
C compiler: gcc (linux, x86_64, 13.3.0)
Garbage collector: Serial GC (max heap size: 80% of RAM)
Build resources:
- 4.74GB of memory (75.6% of system memory, in container)
- 2 thread(s) (100.0% of 2 available processor(s), determined at start)
[2/8] Performing analysis... [*****] (54.9s @ 0.44GB)
3,771 types, 4,434 fields, and 17,148 methods found reachable
1,227 types, 36 fields, and 475 methods registered for reflection
[6/8] Compiling methods... [******] (41.2s @ 0.40GB)
16.90MB in total image size, 15.93MB in total file size
7.2s (5.8% of total time) in 669 GCs | Peak RSS: 1.18GB | CPU load: 1.42
Finished generating 'app-native' in 2m 3s.
| ビルド | かかった時間 | ビルド中の最大メモリ | 実行ファイル |
|---|---|---|---|
| 1回目 | 2分3秒 | 1.18GB | 15.93MB |
| 2回目(コードを足した) | 2分49秒 | 1.18GB | 15.93MB |
| 3回目(メタデータを足した) | 1分37秒 | 1.17GB | 15.93MB |
-Ob(速くビルドする設定) |
1分33秒 | 1.16GB | 16.19MB |
時間の大半は「解析(たどれるコードを探す)」と「コンパイル」です。この小さなプログラムでも 2 CPU で 1分半〜3分かかり、メモリは 1GB を超えました。 Spring Boot のアプリは依存が多いので、時間とメモリはこれより大きくなります。Boot のドキュメントでも、Docker Desktop(macOS)でビルドするならメモリを 8GB 以上に増やすよう書かれています。CI では、ビルド用のマシンのメモリと時間の上限を先に確かめておきます。
できた実行ファイルは、OS の C ライブラリと zlib だけに依存していました。
$ ldd app-native
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6
実測:起動時間とメモリ
起動してから /hello が最初に応答を返すまでの時間と、そのときのメモリ(RSS、プロセスが実際に使っている物理メモリ)を、5回ずつ測りました。
| 実行方法 | 最初の応答まで(5回) | 中央値 | 最初の応答のあとの RSS | 200回応答したあとの RSS |
|---|---|---|---|---|
| OpenJDK 25.0.4 の JVM | 571・324・333・337・261ms | 333ms | 56.7MB | 70.3MB |
| GraalVM 25.0.2 の JVM | 431・425・278・488・404ms | 425ms | 117.1MB | 132.8MB |
| ネイティブイメージ | 10・24・9・14・10ms | 10ms | 17.5MB | 44.0MB |
起動は約30分の1、起動直後のメモリは約3分の1になりました。 一方で、200回応答したあとの RSS は 44.0MB まで増えました。ネイティブイメージの既定の GC は Serial GC で、ビルドのログでは最大ヒープは「RAM の 80%」です。コンテナで動かすなら、-Xmx を付けて上限を決めておきます(ビルドの推奨にも「HEAP: Set max heap」と出ました)。
Spring Boot のアプリでの数字は、Spring Boot のドキュメントの例では次のように出ています。
:: Spring Boot :: (v4.1.1)
........ Started MyApplication in 0.08 seconds (process running for 0.095)
制約の一覧
Spring Boot 4.1.1 のリファレンスと GraalVM の前提から、気をつける点をまとめました。
| 制約 | 何が起きるか | 対処 |
|---|---|---|
@Profile・プロファイル別の設定 |
AOT 処理の時点の条件で Bean が決まる。実行時にプロファイルを変えても、Bean の有無は変わらない | ビルド時にプロファイルを指定する(下) |
@ConditionalOnProperty・.enabled の設定 |
実行時に値を変えても Bean は増減しない | ビルド時の値で決める |
| 文字列からクラスを読むリフレクション | ClassNotFoundException など(下で再現) |
RuntimeHints か到達可能性メタデータ |
| 文字列で名前を組み立てるリソース | getResourceAsStream が null |
hints.resources().registerPattern(…) |
| 動的プロキシ(JDK Proxy) | 登録されていない組み合わせは作れない | hints.proxies().registerJdkProxy(…) |
| Java のシリアライズ | 登録した型だけ | registerJavaSerialization |
| クラスパスの変更 | ビルド後に jar を足しても読まれない | ビルドし直す |
| 実行時のバイトコード生成・Java エージェント | JVM と同じようには使えない(バイトコードを書き換える APM など) | ネイティブに対応した計測の方法を選ぶ |
ネストした @ConfigurationProperties |
@NestedConfigurationProperty が無いと値が入らない |
注釈を付け、public の getter・setter を用意 |
| クロスコンパイル | できない | OS・CPU ごとにビルドする |
プロファイルの制約は、いちばん気づきにくいものです。 Boot のドキュメントでは、AOT 処理は @Conditional をビルド時の環境で評価し、プロファイルも条件で実装されているので影響を受ける、と説明されています。ビルド時のプロファイルは次のように指定します。
// build.gradle.kts
tasks.withType<org.springframework.boot.gradle.tasks.aot.ProcessAot>().configureEach {
args("--spring.profiles.active=prod")
}
<!-- pom.xml の native プロファイルの中 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>process-aot</id>
<configuration>
<profiles>prod</profiles>
</configuration>
</execution>
</executions>
</plugin>
ドキュメントでは、Bean の条件に関係しない設定値だけを変えるプロファイルは、制約なく使えるとされています。URL やタイムアウトの値を application-prod.yml で変える使い方はそのまま動きます。プロファイルの基本は、関連記事「Spring Bootプロファイル切り替え|application.ymlと優先順位」で扱っています。
再現:リフレクションが見つからない
文字列で受け取ったクラス名を Class.forName で読むコードです。
static void reflect(String className) throws Exception {
Class<?> c = Class.forName(className); // 文字列から読む
Object o = c.getDeclaredConstructor().newInstance();
System.out.println("reflect: " + c.getMethod("greet").invoke(o));
}
JVM では動き、ネイティブイメージでは同じクラスがあるのに見つかりませんでした。
$ java -jar app.jar reflect app.Greeter
reflect: hello from Greeter
$ ./app-native reflect app.Greeter
Exception in thread "main" java.lang.ClassNotFoundException: app.Greeter
at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:327)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:297)
at java.base@25.0.2/java.lang.Class.forName(DynamicHub.java:1708)
...
at app.App.reflect(App.java:28)
リソースも、名前を実行時に組み立てると入りませんでした。
$ java -jar app.jar resource2 /META-INF/MANIFEST.MF
resource2: Manifest-Version: 1.0
$ ./app-native resource2 /META-INF/MANIFEST.MF
resource2: null(見つからない)
一方、名前を定数で書いたもの(Proxy.newProxyInstance(…, new Class<?>[]{Runnable.class}, …) や getResourceAsStream("/config.properties"))は、何も登録しなくてもネイティブで動きました。GraalVM がビルド時に定数を読み取って登録するためです。問題になるのは、名前が実行時に決まるときです。
直し方1:トレースエージェントで集める
JVM で動かしながら、リフレクションなどの使われ方を記録する「トレースエージェント」があります。
java -agentlib:native-image-agent=config-merge-dir=src/main/resources/META-INF/native-image/app \
-jar app.jar reflect app.Greeter
GraalVM 25.0.2 では、reachability-metadata.json という1つのファイルに次のように記録されました。
{
"reflection": [
{ "type": "app.Greeter",
"methods": [ { "name": "<init>", "parameterTypes": [] },
{ "name": "greet", "parameterTypes": [] } ] },
{ "type": { "proxy": ["java.util.concurrent.Callable"] } }
],
"resources": [
{ "glob": "META-INF/MANIFEST.MF" }
]
}
このファイルを jar の META-INF/native-image/ の下に入れてビルドし直すと、reflect: hello from Greeter と動き、リソースも読めました。登録したぶん、ビルドログの「registered for reflection」が 1,227 types から 1,231 types に増えました。
Boot のドキュメントでは、Spring Boot のアプリなら -Dspring.aot.enabled=true を付けてエージェントを動かし、出てきたファイルを src/main/resources/META-INF/native-image/ に置く手順が示されています。エージェントは「動かした道筋」しか記録しないので、めったに通らない処理(エラー処理など)も動かしておきます。
直し方2:RuntimeHints をコードで書く(Spring)
Spring のアプリでは、RuntimeHintsRegistrar を実装して @ImportRuntimeHints で読み込ませます。Spring Boot のドキュメントの例をもとにした形です。
package com.example.nativeimage;
import java.lang.reflect.Method;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.util.ReflectionUtils;
@Configuration
@ImportRuntimeHints(AppRuntimeHints.Registrar.class)
public class AppRuntimeHints {
static class Registrar implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
// クラスごと:コンストラクタとメソッドを呼べるようにする
hints.reflection().registerType(Greeter.class,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS);
// メソッドを1つだけ
Method m = ReflectionUtils.findMethod(Greeter.class, "greet");
hints.reflection().registerMethod(m, ExecutableMode.INVOKE);
// リソース(パターンで指定)
hints.resources().registerPattern("templates/mail/*.txt");
// JDK の動的プロキシ(インターフェースの組み合わせ)
hints.proxies().registerJdkProxy(PaymentGateway.class);
// Java のシリアライズ
hints.reflection().registerJavaSerialization(CartSnapshot.class);
}
}
}
| 登録するもの | メソッド | 例 |
|---|---|---|
| クラス(メンバーをまとめて) | reflection().registerType(型, MemberCategory…) |
INVOKE_DECLARED_CONSTRUCTORS など |
| メソッド1つ | reflection().registerMethod(Method, ExecutableMode.INVOKE) |
上の例 |
| リソース | resources().registerPattern("…") |
templates/mail/*.txt |
| 動的プロキシ | proxies().registerJdkProxy(インターフェース…) |
組み合わせごとに1回 |
| シリアライズ | reflection().registerJavaSerialization(型) |
Serializable のクラス |
| JSON の変換に使うクラス | @RegisterReflectionForBinding(型.class) |
RestClient で受け取る DTO |
Spring Framework 7.0 のソースでは、MemberCategory の INTROSPECT_PUBLIC_METHODS・PUBLIC_FIELDS・DECLARED_CLASSES などが @Deprecated(since = "7.0", forRemoval = true) になっています。7.0 で使うのは INVOKE_…(呼べるようにする)と ACCESS_…(フィールドを読み書きできるようにする)の系統です。
@RestController の引数・戻り値の型は、Spring が自動でヒントを作ります。 Boot のドキュメントでは、RestClient・WebClient・RestTemplate を直接使って JSON を受け取る型には、@RegisterReflectionForBinding が必要になることがあると書かれています。
@Service
@RegisterReflectionForBinding({WeatherResponse.class, WeatherResponse.Daily.class})
public class WeatherClient {
private final RestClient restClient;
WeatherClient(RestClient.Builder builder) { this.restClient = builder.baseUrl("https://api.example.com").build(); }
public WeatherResponse today() {
return restClient.get().uri("/today").retrieve().body(WeatherResponse.class);
}
}
ヒントをテストする
ビルドに数分かかるので、ヒントの登録は通常の単体テストで先に確かめます。Boot のドキュメントで紹介されている RuntimeHintsPredicates を使います。
class AppRuntimeHintsTests {
@Test
void registersHints() {
RuntimeHints hints = new RuntimeHints();
new AppRuntimeHints.Registrar().registerHints(hints, getClass().getClassLoader());
assertThat(RuntimeHintsPredicates.reflection().onType(Greeter.class)).accepts(hints);
assertThat(RuntimeHintsPredicates.resource().forResource("templates/mail/welcome.txt")).accepts(hints);
assertThat(RuntimeHintsPredicates.proxies().forInterfaces(PaymentGateway.class)).accepts(hints);
}
}
AssertJ の書き方は、関連記事「AssertJの使い方|assertThatの早見表(文字列・リスト・例外・日付)」で扱っています。
直し方3:ライブラリのメタデータを使う
自分のコードではなく、ライブラリの中のリフレクションで落ちることもあります。Boot のドキュメントでは、Spring 自体はサードパーティのライブラリ向けのヒントを持たず、GraalVM の到達可能性メタデータのリポジトリ(oracle/graalvm-reachability-metadata)に頼る、と説明されています。Maven の native プロファイルは、このメタデータを自動でダウンロードします。そこにも無ければ、トレースエージェントか RuntimeHints で足します。
よくあるエラー
| 症状・メッセージ | 原因 | 対処 |
|---|---|---|
ClassNotFoundException(JVM では動く) |
文字列で読むクラスが登録されていない | エージェントか registerType |
getResourceAsStream が null |
リソースが実行ファイルに入っていない | registerPattern |
MissingReflectionRegistrationError など、登録不足を示すエラー(GraalVM の設定しだいで出る) |
リフレクションの登録不足 | エラーに出た型を登録する |
| JSON の項目が空・変換できない | DTO のリフレクション登録が無い | @RegisterReflectionForBinding |
| 本番で別プロファイルの Bean が無い・余る | Bean の構成がビルド時のプロファイルで決まった | processAot にプロファイルを渡してビルドし直す |
| ビルドが途中で落ちる・終わらない | メモリ不足(手元の小さなプログラムでも最大 1.18GB) | ビルドのマシン・Docker のメモリを増やす |
native-image が見つからない |
GraalVM ではない JDK を使っている | GraalVM・Liberica NIK を使う |
| ビルドで C コンパイラのエラー | gcc・zlib・Windows の Build Tools が無い | 前提の表のものを入れる |
| macOS で作った実行ファイルが Linux で動かない | クロスコンパイルはできない | 目的の OS でビルドする(コンテナ・CI) |
| コンテナでメモリが増え続ける | 最大ヒープの既定が「RAM の 80%」 | 起動時に -Xmx を付ける |
よくある質問
Q. JVM とネイティブ、どちらのイメージを使うかはどう決めますか。 A. 起動の速さとメモリが料金・待ち時間に直結するなら(サーバーレス・台数の増減が激しい)ネイティブ、長く動かしてスループットを出す API なら JVM が目安です。ビルド時間と制約の対応にかかる手間も比べます。
Q. Docker のイメージにするにはどうしますか。
A. gradle bootBuildImage・mvn -Pnative spring-boot:build-image で、Buildpacks がネイティブのイメージを作ります。ドキュメントでは、この方法ではローカルに GraalVM は要らず、Docker が必要です。Dockerfile で作る場合は、ビルド用のステージで GraalVM を使い、実行用のステージに実行ファイルだけを入れます。Docker の基本は、関連記事「Dockerfileの書き方|軽く・速く・安全なイメージを作るベストプラクティス」で扱っています。
Q. Boot 3 から 4 に上げると何が変わりますか。
A. Buildpacks を使う場合、Boot 4.1.1 のドキュメントでは JDK 25 以上でビルドする必要があります。Spring Framework 7.0 では MemberCategory の一部が非推奨になりました。移行全体は、関連記事「Spring Boot 4移行の手順|3から4の変更点一覧とチェックリスト」で扱っています。
まとめ
- 作り方は Gradle:
nativeCompile、Maven:-Pnative native:compile。プラグインはorg.graalvm.buildtools.native(Boot 4.1.1 は 1.1.8) - 小さなプログラムの実測で、起動 333ms → 10ms、起動直後のメモリ 56.7MB → 17.5MB
- そのかわり、ビルドは 1分半〜3分、ビルド中のメモリは 1.18GB(2 CPU・小さなプログラム)
- プロファイルと
@ConditionalOnPropertyはビルド時に決まる。本番のプロファイルはprocessAotに渡す - 名前が実行時に決まるリフレクション・リソースは、トレースエージェントか
RuntimeHintsで登録する - 先に
-Dspring.aot.enabled=trueで JVM のまま確かめると、ビルドの回数を減らせる
参考・出典
確認日はいずれも 2026年10月13日です。
- Spring Boot Reference「Introducing GraalVM Native Images」:https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html
- Spring Boot Reference「Advanced Native Images Topics」:https://docs.spring.io/spring-boot/reference/packaging/native-image/advanced-topics.html
- Spring Boot How-to「Developing Your First GraalVM Native Application」:https://docs.spring.io/spring-boot/how-to/native-image/developing-your-first-application.html
- Spring Boot How-to「Ahead-of-Time Processing」:https://docs.spring.io/spring-boot/how-to/aot.html
- Spring Boot v4.1.1 ソース(
gradle.properties・ドキュメントのサンプル):https://github.com/spring-projects/spring-boot/tree/v4.1.1 - Spring Framework v7.0.9 ソース(
org.springframework.aot.hint):https://github.com/spring-projects/spring-framework/tree/v7.0.9 - GraalVM CE 25.0.2 リリース:https://github.com/graalvm/graalvm-ce-builds/releases/tag/jdk-25.0.2
- GraalVM Reachability Metadata:https://github.com/oracle/graalvm-reachability-metadata
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Spring BootのGraalVMネイティブイメージ|作り方と制約」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。