2018年5月,GraalVM 1.0 正式发布。本文可以理解为 GraalVM 的入门指南。
一、GraalVM 简介
GraalVM 是一个高性能的运行时环境,由 Oracle Labs 开发。它不仅仅是一个 JVM,更是一个“通用虚拟机”,支持运行多种语言(Java, JavaScript, Python, Ruby, R, LLVM-based languages 等),并提供了革命性的 Native Image 技术,可以将 Java 应用程序提前编译(AOT – Ahead-Of-Time)为独立的、启动极快、内存占用低的本地可执行文件。
核心优势:
- 高性能: GraalVM 的 JIT 编译器(Graal Compiler)一般能生成比传统 HotSpot C2 编译器更优的机器码。
- 多语言支持 (Polyglot): 在同一个运行时中无缝混合使用多种语言。
- 原生镜像 (Native Image): 将 Java 应用编译成本地可执行文件,实现:
- 极速启动 (毫秒级)
- 极低内存占用
- 更小的分发体积 (无需捆绑完整 JVM)
- 更高的安全性 (减少攻击面)
- 工具链: 提供强劲的监控、调试和分析工具 (GraalVM Dashboard, Ideal Graph Visualizer 等)。
二、安装 GraalVM
1. 下载
访问 GraalVM 官网下载页面:https://www.graalvm.org/downloads/
- 选择版本:
- GraalVM Community Edition (CE): 开源免费,适合大多数用户和生产环境。
- GraalVM Enterprise Edition (EE): 商业版,提供额外的性能优化、安全更新和技术支持。
- 选择 Java 版本: 一般选择与你项目兼容的版本(如 Java 17, Java 21)。
- 选择操作系统: Linux, macOS, Windows。
- 选择架构: x86_64, aarch64 (ARM64)。
2. 安装
- Linux/macOS:
- 下载 .tar.gz 文件。
- 解压到你希望安装的目录(例如 ~/graalvm/ 或 /opt/graalvm/)。
tar -xzf graalvm-community-jdk-<version>_<os>-<arch>.tar.gz -C /path/to/install/dir
- 设置 JAVA_HOME 环境变量指向 GraalVM 目录。
export JAVA_HOME=/path/to/install/dir/graalvm-<version>
export PATH=$JAVA_HOME/bin:$PATH
- (可选但推荐) 使用 gu (GraalVM Updater) 安装你需要的额外组件(如 native-image, Python, R, Ruby, LLVM 工具链等):
# 查看可用组件
gu available
# 安装 native-image (Native Image 编译器)
gu install native-image
# 安装其他语言支持 (例如 Python)
gu install python
- Windows:
- 下载 .zip 文件。
- 解压到你希望安装的目录(例如 C:Program FilesGraalVM)。
- 设置 JAVA_HOME 环境变量指向 GraalVM 目录(例如 C:Program FilesGraalVMgraalvm-<version>)。
- 将 %JAVA_HOME%in 添加到系统 PATH 环境变量中。
- (可选但推荐) 使用 gu.cmd 安装组件:
gu.cmd available
gu.cmd install native-image
gu.cmd install python
3. 验证安装
打开终端或命令提示符:
# 检查 Java 版本和供应商
java -version
# 输出应包含 "GraalVM" 字样
# 检查 gu (GraalVM Updater) 是否可用
gu list
# 列出已安装的组件
# 如果安装了 native-image,检查其版本
native-image --version
三、使用 GraalVM 作为标准 JVM
安装完成后,你可以像使用普通 JDK 一样使用 GraalVM 来编译和运行 Java 应用。GraalVM 默认使用其高性能的 Graal JIT 编译器。
# 编译 Java 文件
javac HelloWorld.java
# 运行 Java 程序 (使用 Graal JIT)
java HelloWorld
启用 Graal JIT 编译器 (如果默认未启用):
在较新版本的 GraalVM CE 中,Graal JIT 一般是默认的。如果需要显式启用或在其他 JVM 上使用 Graal JIT,可以添加 JVM 参数:
java -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler HelloWorld
四、Native Image (原生镜像) – 核心功能
这是 GraalVM 最引人注目的特性。它将 Java 字节码 AOT 编译成本地机器码。
1. 基本使用
假设你有一个简单的 Java 类 HelloWorld.java:
// HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, GraalVM Native Image!");
}
}
步骤:
- 编译成字节码:
javac HelloWorld.java
- 生成原生可执行文件:
native-image HelloWorld
- 这会生成一个名为 helloworld (Linux/macOS) 或 helloworld.exe (Windows) 的可执行文件。
- native-image 工具会分析 HelloWorld.class,找出所有可达的代码(包括依赖的 JDK 类),并将其编译成本地代码。这个过程称为 构建时 (Build Time)。
- 运行原生可执行文件:
./helloworld # Linux/macOS
helloworld.exe # Windows
- 你会立即看到输出,启动速度极快!
2. Native Image 关键概念与配置
- 封闭世界假设 (Closed World Assumption): Native Image 在构建时需要知道应用程序运行时可能执行的所有代码路径。它通过静态分析来确定“可达性”。这意味着:
- 反射 (Reflection)、动态代理 (Dynamic Proxy)、JNI、序列化 (Serialization) 等动态特性需要显式配置,由于静态分析无法预测它们。
- 资源文件 (Resources) 也需要显式配置才能被打包进原生镜像。
- 配置文件 (Configuration Files): 一般放在 META-INF/native-image/ 目录下(相对于 classpath 根目录)。
- reflect-config.json: 配置反射访问的类、方法、字段。
- resource-config.json: 配置需要包含的资源文件。
- serialization-config.json: 配置序列化。
- jni-config.json: 配置 JNI。
- proxy-config.json: 配置动态代理。
- native-image.properties: 配置默认的 native-image 构建参数。
- 生成配置文件 (推荐方式): 手动编写配置文件很繁琐且容易出错。GraalVM 提供了 Tracing Agent 来自动生成大部分配置。
- 使用普通 JVM (GraalVM 或其他) 运行你的应用,并附加 Agent:
java -agentlib:native-image-agent=config-output-dir=META-INF/native-image -jar your-app.jar
# 或者对于类
java -agentlib:native-image-agent=config-output-dir=META-INF/native-image HelloWorld
- 充分执行你的应用程序,触发所有你希望在原生镜像中支持的代码路径(包括反射、资源访问等)。
- Agent 会在 META-INF/native-image/ 目录下生成相应的 *-config.json 文件。
- 将这些配置文件打包进你的 JAR 文件或放在 classpath 中。
- 再次运行 native-image 命令,它会自动读取这些配置。
- 常用 native-image 参数:
- -H:Name=<output_name>: 指定输出文件名。
- -cp <classpath> / -jar <jarfile>: 指定 classpath 或 JAR 文件。
- –no-fallback: 如果构建失败(例如由于无法解析反射),直接报错而不是回退到包含完整的 JVM。强烈提议在 CI/CD 中使用此选项以确保构建的可靠性。
- –verbose: 输出详细构建信息。
- -D<key>=<value>: 传递系统属性给构建过程(注意:不是运行时!)。
- –initialize-at-build-time=<class/package>: 在构建时初始化指定的类或包(提高启动速度,但需确保安全)。
- –initialize-at-run-time=<class/package>: 强制在运行时初始化指定的类或包(解决构建时初始化导致的问题)。
- –enable-url-protocols=http,https: 启用特定的 URL 协议处理器。
- –enable-preview: 启用 JDK 预览特性(如果使用)。
- –gc=<gc>: 指定垃圾回收器(如 g1, epsilon – 无 GC,适用于短生命周期应用)。
3. 与构建工具集成
- Maven:推荐使用官方插件 org.graalvm.buildtools:native-maven-plugin。
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.10.1</version> <!-- 请检查最新版本 -->
<extensions>true</extensions>
<configuration>
<!-- 可选配置 -->
<imageName>my-app</imageName>
<buildArgs>
<arg>--no-fallback</arg>
<arg>--verbose</arg>
</buildArgs>
</configuration>
</plugin>
构建命令:
构建命令:
- Gradle:推荐使用官方插件 org.graalvm.buildtools.native。
plugins {
id 'org.graalvm.buildtools.native' version '0.10.1' // 请检查最新版本
}
graalvmNative {
binaries {
main {
imageName = 'my-app'
buildArgs.add('--no-fallback')
buildArgs.add('--verbose')
// 其他配置...
}
}
}
构建命令:
# 生成配置 (可选)
./gradlew run --args='--agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image'
# 构建原生镜像
./gradlew nativeCompile
4. Native Image 限制与挑战
- 动态特性支持: 反射、动态类加载、JNI 等需要额外配置,增加了复杂性。
- 构建时间长: AOT 编译过程比普通 JVM 启动慢得多,尤其是在大型项目中。
- 构建内存消耗大: Native Image 构建过程可能消耗大量内存。
- 平台依赖: 生成的原生镜像是平台特定的(Linux 生成的不能在 Windows 运行)。需要为每个目标平台单独构建。
- 调试困难: 调试原生镜像比调试 JVM 上的 Java 应用更复杂(虽然 GraalVM 也在改善工具支持)。
- 某些库不兼容: 并非所有 Java 库都能无缝工作在 Native Image 下,尤其是那些重度依赖动态特性的库(如某些 ORM、依赖注入框架的旧版本)。不过,主流框架(Spring Boot 3+, Quarkus, Micronaut, Helidon)目前都对 Native Image 提供了良好支持。
五、多语言编程 (Polyglot)
GraalVM 允许你在同一个进程中混合使用多种语言。
1. 通过命令行
安装相应语言组件后,可以直接运行:
# 运行 JavaScript
js myscript.js
# 运行 Python (GraalPython)
graalpython myscript.py
# 运行 Ruby (TruffleRuby)
truffleruby myscript.rb
# 运行 R
Rscript myscript.R
2. 在 Java 中嵌入其他语言
使用 org.graalvm.polyglot API。
import org.graalvm.polyglot.*;
public class PolyglotExample {
public static void main(String[] args) {
try (Context context = Context.create()) {
// 执行 JavaScript 代码
Value result = context.eval("js", "21 + 21");
System.out.println(result.asInt()); // 输出: 42
// 执行 Python 代码
Value pyResult = context.eval("python", "def add(a, b): return a + b
add(10, 20)");
System.out.println(pyResult.execute(10, 20).asInt()); // 输出: 30
}
}
}
3. 语言互操作
不同语言的对象和函数可以相互调用。
// Java 调用 JavaScript 函数
Value jsFunction = context.eval("js", "(function(x) { return x * 2; })");
int doubled = jsFunction.execute(5).asInt(); // doubled = 10
// JavaScript 调用 Java 对象
context.getBindings("js").putMember("javaObj", someJavaObject);
context.eval("js", "javaObj.someMethod();");
六、性能监控与调试
- JVM 模式: 可以使用标准的 Java 工具(jvisualvm, jconsole, jcmd, jstack, jmap)以及 GraalVM 特有的 GraalVM Dashboard (需要安装 graalvm-dashboard 组件)。
- Native Image 模式:
- 调试: 可以生成带有调试信息的可执行文件 (-g 参数),然后使用 gdb (Linux) 或 lldb (macOS) 进行调试。Java 堆栈跟踪信息在原生镜像中也是可用的。
- 性能分析: 可以使用标准的系统性能分析工具(如 perf (Linux), Instruments (macOS))。GraalVM 也支持生成可用于 perf 分析的符号映射文件 (–no-debug-info 时可能需要 –enable-monitoring=heapdump,perf 等选项,具体请查阅最新文档)。
七、最佳实践与提议
- 从简单开始: 先用简单的 “Hello World” 体验 Native Image。
- 善用 Tracing Agent: 这是处理反射等动态特性的最有效方法。
- 使用 –no-fallback: 在正式构建中使用,确保构建的确定性和可靠性。
- 关注框架支持: 选择对 Native Image 有良好支持的框架(如 Spring Boot 3+)。
- 管理构建资源: Native Image 构建需要时间和内存,确保你的构建机器有足够的资源。
- CI/CD 集成: 将 Native Image 构建集成到你的持续集成/持续部署流水线中。
- 监控与日志: 确保你的应用在原生镜像中也能输出足够的日志和监控指标。
- 查阅官方文档: GraalVM 发展迅速,官方文档(https://www.graalvm.org/docs/)是最权威和最新的信息来源。
- 社区支持: 遇到问题时,可以查阅 Stack Overflow、GraalVM GitHub Issues 或相关社区论坛。
八、总结
GraalVM 是一个强劲的工具,尤其其 Native Image 技术为 Java 应用在云原生、Serverless、CLI 工具等场景带来了革命性的改善(快速启动、低内存)。虽然存在一些学习曲线和限制(主要是动态特性配置),但随着生态系统的成熟(框架支持、工具完善),它正变得越来越易用和强劲。掌握 GraalVM,特别是 Native Image,将成为现代 Java 开发者的一项重大技能。
希望这份指南能协助你顺利开启 GraalVM 之旅!
收藏了,感谢分享