GraalVM 使用详解:JIT、Native Image 与多语言运行时
GraalVM 是 Oracle 主导的高性能 JDK 发行版,核心由三部分组成:Graal 编译器(用 Java 编写的 JIT 编译器,可替代 HotSpot 的 C2)、Native Image(AOT 提前编译,将 Java 程序编译为本地可执行文件)、Truffle 多语言框架(支持在同一进程中运行 JavaScript、Python 等语言)。与普通 OpenJDK 相比,GraalVM 的主要价值体现在两点:作为 JIT 编译器带来更高的峰值性能,以及通过 Native Image 实现毫秒级启动和极低的内存占用。
安装 GraalVM
推荐使用 SDKMAN! 管理多个 JDK 版本:
1 | |
输出示例:
1 | |
也可以从 GraalVM 官网或 GitHub Releases 直接下载压缩包,解压后设置 JAVA_HOME 指向解压目录即可。
两种运行模式
JIT 模式:作为普通 JDK 使用
GraalVM 本身就是完整的 JDK,可以直接编译运行 Java 程序。默认情况下,JVM 使用 Graal 编译器替代 C2 作为顶层 JIT 编译器,在部分负载下(尤其是大量使用 Stream、Lambda、装箱操作的代码)峰值性能优于 C2。
1 | |
如需对比,可以使用 -XX:-UseJVMCICompiler 回退到 C2 编译器。
Native Image:AOT 编译为本地可执行文件
Native Image 是 GraalVM 的核心卖点。它在构建期执行封闭世界假设(closed-world assumption)分析:从入口方法出发做可达性分析,只把可达的类、方法和资源编译进一个独立的本地可执行文件,运行时不再需要 JVM。
主要收益:
- 启动极快:通常在几十毫秒内启动,适合 Serverless、CLI 工具、短生命周期任务。
- 内存占用低:无需加载 JVM 和 JIT 编译,常驻内存可降至传统 JVM 的 1/5 甚至更低。
- 独立分发:产物不依赖 JDK,镜像体积极小(配合静态链接可跑在
scratch镜像中)。
代价:
- 构建耗时长:可达性分析 + AOT 编译通常需要数分钟。
- 吞吐量可能略低:缺少 JIT 的运行期画像优化(PGO 可缓解)。
- 动态特性受限:反射、动态代理、资源加载、JNI 等需要显式配置。
Native Image 快速上手
以最简单的程序为例:
1 | |
编译并生成可执行文件:
1 | |
常用构建参数:
1 | |
--no-fallback 在生产构建中必须加上:默认情况下构建失败会生成一个回退到 JVM 运行的镜像,掩盖问题。
反射与动态特性配置
Native Image 的封闭世界假设意味着所有反射访问必须在构建期声明。以下代码在 JVM 上正常,但直接编译成 native image 会在运行期报错:
1 | |
解决方式有两种:
使用 Tracing Agent 自动生成配置
在 JVM 模式下挂载 agent 运行应用,自动记录所有反射、资源、代理、序列化的使用,生成配置文件:
1 | |
生成的 META-INF/native-image/ 目录下包含:
reflect-config.json:反射配置resource-config.json:资源文件配置proxy-config.json:动态代理配置serialization-config.json:序列化配置
这些文件放入类路径后,native-image 会自动读取。
手工编写配置
对于无法完整运行应用的场景,可手工编写 reflect-config.json:
1 | |
构建时通过 -H:ReflectionConfigurationFiles=reflect-config.json 指定。
使用 Maven 插件构建
GraalVM 官方提供 native-maven-plugin,将 native 编译集成到构建流程:
1 | |
执行构建:
1 | |
产物输出在 target/ 目录下,可直接执行。
Spring Boot 4 集成
Spring Boot 4.0(2025 年 11 月发布,基于 Spring Framework 7、Jakarta EE 11)原生支持 GraalVM,通过 Spring AOT 引擎在构建期处理 Bean 定义、条件注解和配置绑定,解决了框架大量反射使用与封闭世界假设的冲突。Spring 官方维护的 Reachability Metadata 仓库(graalvm-reachability-metadata)覆盖了绝大多数主流第三方库。
与 Spring Boot 3 相比,4.x 在 native 方向的关键变化是工具链基线提升,而非编程模型变化:
- Native Image 构建要求 GraalVM 25+(JVM 模式运行仍只需 Java 17+),低于该基线的镜像启动时会被拒绝。
- Native Build Tools 升级到 0.11,构建命令保持不变。
spring-jcl已移除,改用普通commons-logging;Log4j2 的 native 支持自 4.0.1 起可用,Logback 保持支持。- 因 Servlet 6.1 基线要求,Undertow 支持被移除,相关应用需迁移到 Tomcat/Jetty。
构建 native 版本 Spring Boot 应用:
1 | |
辅助工作流(4.x 官方推荐):
1 | |
注意 Spring AOT 的构建期固化约束:类路径在构建期固定,@ConditionalOnProperty 等条件注解在 AOT 处理时已求值,端口、日志级别等普通配置仍可在运行期调整,但 Bean 图结构不能靠部署期配置改变。
此外,Spring Boot 4 文档新增了与 GraalVM 无关的 Java 25 AOT Cache 方案:通过训练缓存加速 JVM 启动(官方建议 Java 25+ 时优先于 CDS),适合想要更快启动但又不想承担 native 兼容性约束的场景。
实测效果(典型 REST 应用):
| 指标 | JVM 模式 | Native Image |
|---|---|---|
| 启动时间 | 2~4 秒 | 0.05~0.1 秒 |
| 常驻内存 | 200~400 MB | 50~100 MB |
| 镜像大小 | 200 MB+ | 50~80 MB |
| 峰值吞吐 | 高 | 略低(可用 PGO 弥补) |
适用场景
Native Image 并非银弹,选型时按场景判断:
适合:
- Serverless / FaaS 函数:冷启动时间直接决定成本和延迟。
- CLI 命令行工具:用户无法接受秒级启动等待。
- 容器化微服务:低内存占用意味着更高的部署密度。
- 短生命周期批处理任务:JVM 预热成本占比过高。
不适合:
- 长运行、吞吐量敏感的服务:JIT 的运行期优化仍有优势。
- 重度依赖反射、动态类加载的框架或库:配置成本可能很高。
- 需要 JVM 诊断工具链(JFR、Arthas 等)深度排障的系统。
多语言支持
GraalVM 的 Truffle 框架允许在 JVM 上运行其他语言,并实现跨语言互操作:
1 | |
该特性适用于规则引擎、脚本扩展、插件系统等需要嵌入脚本的场景,纯 Java 应用一般用不到。
总结
GraalVM 提供了两条使用路径:作为 JDK 替换获得更好的 JIT 性能,以及通过 Native Image 获得极致的启动速度和内存效率。实践中 Spring Boot 4 + Native Image 已成为云原生 Java 的主流方案之一,但引入前应评估框架兼容性、构建成本和诊断工具的缺失;从 Boot 3 升级时需将 native 构建工具链提升到 GraalVM 25+ 并重建全部 AOT 元数据。对于新项目,建议从第一天起就用 --no-fallback 构建并接入 CI,尽早暴露反射配置问题。






