返回文章列表
JVM虚拟机
JVMGraalVMAOTnative-image

11GraalVM

1.什么是GraalVM

GraalVM 是 Oracle 官方推出的一款高性能 JDK,使用它可以享受比 OpenJDK 或 OracleJDK 更好的性能。官方网址:https://www.graalvm.org/。

官方标语是 "Build faster, smaller, leaner applications",核心价值体现在:

  • 更低的 CPU、内存使用率
  • 更快的启动速度,无需预热即可获得最好的性能
  • 更好的安全性、更小的可执行文件
  • 支持多种框架:Spring Boot、Micronaut、Helidon 和 Quarkus
  • 多家云平台支持
  • 通过 Truffle 框架运行 JS、Python、Ruby 等其他语言

GraalVM 概览

GraalVM 分为社区版(Community Edition)和企业版(Enterprise Edition)。企业版相较社区版在性能上有更多优化:

特性 描述 社区版 企业版
收费 是否收费 免费 收费
G1 垃圾回收器 使用 G1 优化垃圾回收性能 × √
Profile Guided Optimization(PGO) 运行中收集动态数据,进一步优化本地镜像性能 × √
高级优化特性 更多优化技术,降低内存和 GC 开销 × √
高级优化参数 更多的高级优化参数可以设置 × √

版本对比

1.1 社区版环境搭建

以 Linux 下搭建 GraalVM 社区版为例:

  1. 使用 arch 命令查看 Linux 架构。
  2. 根据架构到 https://www.graalvm.org/downloads/ 下载对应版本。
  3. 安装方式与安装普通 JDK 相同:解压文件、设置环境变量。
  4. 使用 java -version 和 HelloWorld 测试 GraalVM 是否可用。

环境测试

2.GraalVM 的两种运行模式

GraalVM 提供 JIT 与 AOT 两种运行模式。

2.1 JIT 即时编译模式

JIT(Just-In-Time)模式的处理方式与 Oracle JDK 类似,满足两个特点:

  • Write Once, Run Anywhere:一次编写,到处运行。
  • 预热之后,通过内置的 Graal 即时编译器优化热点代码,生成比 HotSpot JIT 性能更高的机器码。

可以在 JDK8、JDK21、GraalVM 21(开启 Graal 即时编译器)、GraalVM 21(不开启 Graal 即时编译器)四种环境下运行 JMH 性能测试用例对比性能。

注意:-XX:-UseJVMCICompiler 参数可以关闭 GraalVM 中的 Graal 编译器。

2.2 AOT 提前编译模式

AOT(Ahead-Of-Time)编译器通过源代码为特定平台创建可执行文件,例如在 Windows 下编译完成之后会生成 exe 文件。这种模式生成的文件称为 Native Image 本地镜像,启动之后即可获得最高性能,但不具备跨平台特性,不同平台使用需要单独编译。

AOT 编译原理

制作本地镜像的步骤:

  1. 安装 Linux 环境本地镜像制作需要的依赖库,参考 Native Image 官方手册。
  2. 使用 native-image 类名 制作本地镜像。
  3. 运行本地镜像可执行文件。

AOT 实战

2.3 模式与版本的性能对比

社区版 GraalVM 使用本地镜像模式的性能不如 HotSpot JVM 的 JIT 模式,但企业版的性能相对会高很多。

性能对比

2.4 AOT 模式存在的问题

AOT 模式在启动速度、内存和 CPU 开销上非常有优势,但也会带来几个问题:

  1. 跨平台问题:在不同平台下运行需要编译多次,编译平台的依赖库等环境要与运行平台保持一致。
  2. 编译时间长:使用框架之后,编译本地镜像的时间较长,同时消耗大量 CPU 和内存。
  3. 运行时类创建问题:AOT 编译器在编译时需要知道运行时所有可访问的类,但 Java 中存在反射、动态代理等运行时创建类的技术,这些技术在 Spring 等框架中大量使用。

解决方案:

  1. 使用公有云的 Docker 等容器化平台进行在线编译,确保编译环境和运行环境一致,同时解决编译资源问题。
  2. 使用 SpringBoot3 等已经整合了 GraalVM AOT 模式的框架版本。

AOT 模式问题

2.5 SpringBoot3 实战

SpringBoot3 对 GraalVM 进行了完整的适配,编写 GraalVM 服务推荐使用 SpringBoot3。

  1. 使用 Spring Initializr 在线生成器构建项目。
  2. 编写业务代码。由于 AOT 不支持 @PostConstruct 等反射相关注解,需要改为实现 InitializingBean 接口的 afterPropertiesSet 方法:
@Service
public class UserServiceImpl implements UserService, InitializingBean {
 
    private List<User> users = new ArrayList<>();
 
    @Autowired
    private UserDao userDao;
 
    @Override
    public List<UserDetails> getUserDetails() {
        return userDao.findUsers();
    }
 
    @Override
    public List<User> getUsers() {
        return users;
    }
 
    @Override
    public void afterPropertiesSet() throws Exception {
        //初始化时生成数据
        for (int i = 1; i <= 10; i++) {
            users.add(new User((long) i, RandomStringUtils.randomAlphabetic(10)));
        }
    }
}
  1. 执行 mvn -Pnative clean native:compile 命令生成本地镜像。
  2. 运行本地镜像。

SpringBoot3 项目

3.GraalVM 应用场景

什么场景下需要使用 GraalVM?

  1. 对性能要求比较高的场景,可以使用收费的企业版提升性能。
  2. 公有云的部分服务按 CPU 和内存使用量计费,使用 GraalVM 可以有效降低费用。

使用场景

3.1 Serverless 架构

传统系统架构中,服务器等基础设施的运维、安全、高可用工作都由企业自行完成,存在两个主要问题:

  1. 开销大:包括人力开销和机房建设开销。
  2. 资源浪费:面对秒杀等突发流量冲击,必须提前规划容量准备大量服务器,这些服务器在其他时间处于闲置状态。

传统架构问题

随着虚拟化技术、云原生技术的愈发成熟,云服务商提供了 Serverless 无服务器化架构,企业无需进行服务器的配置和部署,完全由云服务商提供。典型代表有亚马逊 AWS、阿里云等。其特点包括:无需配置部署服务器、按价值付费、自动扩容、高可用与容错。

Serverless 架构

函数计算(FaaS)

Serverless 架构中第一种常见的服务是函数计算(Function as a Service),将一个应用拆分成多个函数,每个函数以事件驱动的方式触发。典型代表有 AWS 的 Lambda、阿里云的 FC。

函数计算

函数计算的主要应用场景:

  1. 小程序、API 服务中的接口:调用频率不高,使用常规服务器架构容易产生资源浪费,Serverless 可以实现按需付费并支持自动伸缩应对突发流量。
  2. 大规模任务处理:如音视频文件转码、审核等,可利用事件机制在文件上传后自动触发对应任务。

函数计算的计费标准中包含 CPU 和内存使用量,所以使用 GraalVM AOT 模式编译出来的本地镜像可以节省更多成本。

函数计算场景

Serverless 应用(基于容器)

函数计算的服务资源比较受限,例如 AWS Lambda 一般无法支持超过 15 分钟的函数执行,所以云服务商提供了基于容器的 Serverless 应用方案:无需手动配置 K8s 中的 Pod、Service 等内容,只需选择镜像就可自动生成应用服务。

服务分类 交付模式 弹性效率 计费模式
函数计算 函数 毫秒级 调用次数 + CPU 内存使用量
Serverless 应用 镜像容器 秒级 CPU 内存使用量

Serverless 应用

部署到阿里云函数计算或 Serverless 应用的步骤相似:

  1. 在项目中编写 Dockerfile 文件:
# Using Oracle GraalVM for JDK 17
FROM container-registry.oracle.com/graalvm/native-image:17-ol8 AS builder
 
# Set the working directory to /home/app
WORKDIR /build
 
# Copy the source code into the image for building
COPY . /build
RUN chmod 777 ./mvnw
 
# Build
RUN ./mvnw --no-transfer-progress native:compile -Pnative
 
# The deployment Image
FROM container-registry.oracle.com/os/oraclelinux:8-slim
 
EXPOSE 8080
 
# Copy the native executable into the containers
COPY --from=builder /build/target/spring-boot-3-native-demo app
ENTRYPOINT ["/app"]
  1. 使用服务器制作镜像,这一步会消耗大量的 CPU 和内存资源,同时 GraalVM 相关镜像服务器在国外,建议使用阿里云的镜像服务器制作 Docker 镜像。
  2. 函数计算:使用函数计算将 Docker 镜像转换成函数服务,绑定域名并测试;Serverless 应用:配置 Serverless 应用,选择容器镜像、CPU 和内存,绑定外网负载均衡并测试。

Serverless 部署

4.参数优化和故障诊断

由于 GraalVM 是一款独立的 JDK,大部分 HotSpot 中的虚拟机参数都不适用,常用参数可参考官方手册。

4.1 内存与垃圾回收参数

  • 社区版只能使用串行垃圾回收器(Serial GC):默认最大 Java 堆大小会设置为物理内存大小的 80%,调整方式为 -Xmx最大堆大小。如果希望在编译期就指定该大小,可在编译时添加参数 -R:MaxHeapSize=最大堆大小。
  • G1 垃圾回收器只能在企业版中使用:开启方式为 --gc=G1,可有效降低垃圾回收的延迟。
  • Epsilon GC:开启方式 --gc=epsilon,它不会产生任何垃圾回收行为,因此没有额外的内存、CPU 开销。如果在公有云上运行的程序生命周期短暂且不产生大量对象,可以使用该垃圾回收器以节省最大资源。

使用 -XX:+PrintGC -XX:+VerboseGC 参数可打印垃圾回收详细信息。

内存参数

4.2 内存快照文件的获取

  1. 编译程序时添加 --enable-monitoring=heapdump 参数(添加到 pom 文件的对应插件中):
<plugin>
   <groupId>org.graalvm.buildtools</groupId>
   <artifactId>native-maven-plugin</artifactId>
   <configuration>
      <buildArgs>
         <arg>--enable-monitoring=heapdump,jfr</arg>
      </buildArgs>
   </configuration>
</plugin>
  1. 运行中使用 kill -SIGUSR1 进程ID 命令创建内存快照文件。
  2. 使用 MAT 分析内存快照文件。

内存快照

4.3 运行时数据的获取

JDK Flight Recorder (JFR) 是一个内置于 JVM 中的工具,可以收集正在运行中的 Java 应用程序的诊断和分析数据,比如线程、异常等内容。GraalVM 本地镜像也支持使用 JFR 生成运行时数据,导出的数据可使用 VisualVM 分析。

  1. 编译程序时添加 --enable-monitoring=jfr 参数(同上 pom 配置)。
  2. 运行程序,添加参数 -XX:StartFlightRecording=filename=recording.jfr,duration=10s。
  3. 使用 VisualVM 分析 JFR 记录文件。

JFR 运行时数据

5.小结

  1. 什么是 GraalVM? GraalVM 是 Oracle 官方推出的一款高性能 JDK,具备两种模式,JIT 模式使用方式与 OracleJDK 相同;使用 AOT 模式制作的本地镜像具备启动速度快、CPU 和内存占用率低的优点,分为免费的社区版和收费的企业版,企业版拥有比社区版更好的性能。
  2. 什么场景下使用 GraalVM?
    • 希望拥有更好的性能,使用 JIT 模式或者升级为企业版。
    • 执行时间较短的业务,使用 GraalVM 生成本地镜像,发布到函数计算云服务。
    • 执行时间较长的业务(如长时间计算任务),使用 GraalVM 生成本地镜像,发布到 Serverless 容器云服务。

小结