GraalVM 介绍
GraalVM概述 - Spring Cloud Alibaba官网
科普文:GraalVM简介-CSDN博客
科普文:OpenJDK vs. GraalVM vs. Amazon Corretto性能测试-CSDN博客
科普文:【方向盘】OpenJDK生态圈-CSDN博客
科普文:Oracle JDK收费后的常见openJDK版本梳理-CSDN博客
Spring Boot 3.0 本次带来最大的改动就是 GraalVM 原生镜像的支持,也是官方文档中强调的他们花费时间精力比较多的部分。 GraalVM 技术作为 JRE 的替代方案,其通过预先编译(Ahead Of Time,AOT)等技术对 Java 应用进行预先编译,让 Spring 在运行应用时掌握更多应用有关的信息,让整个应用启动速度更快。另外,通过编译工具在编译过程中通过消除一些不必要的内容可以让最终的应用更小,占用内存更低。对于一些对启动速度要求非常高的场景,比如 Serverless、FaaS 场景非常友好! 本次 Spring Boot 3.0 直接将其正式从 Spring Native 迁入到 Spring Boot 中来,也预示着该项技术开始逐渐走向成熟,Spring 生态开始迈入 GraalVM 阶段!
GraalVM · GitHub
跟 JVM 编译部署方式相比,GraalVM 具有以下特点:
- 在应用构建阶段,从主入口点就开始进行应用程序的静态分析。
- 创建本机镜像时,通过代码分析,会将无法访问的代码删除,并且不会成为可执行文件的一部分,从而可在一定程度上压缩程序包大小。
- GraalVM 无法直接感知代码的动态元素,因此对于存在反射、序列化和动态代理的应用程序,需要提前提供相关 hint 配置文件,帮助解析应用程序,相关操作过程可参考官方文档。
- 应用程序类路径在构建时是固定的,不能更改。
- 没有惰性类加载,可执行文件中的所有内容都将在启动时加载到内存中。
- 支持的 Java 应用程序在某些方面存在一些限制,因此目前并不能保证之前的 Java 应用都可直接使用 GraalVM 技术进行应用构建,有一定概率会存在不兼容的异常情况。
Spring Cloud Alibaba 2022.0.0.0 版本在 GraalVM测试结果
本次发布的 Spring Cloud Alibaba 2022.0.0.0 版本所包含的部分中间件客户端已完成了构建 GraalVM 原生应用的适配,以下是社区对目前所支持的服务注册与发现模块相关示例应用在升级为 Spring Boot 3.0 以后,使用 GraalVM 构建原生应用镜像做的在启动速度和运行时占用内容相关的测试
(测试过程在 macOS 11.4,2.6 GHz 6-Core Intel Core i7 处理器,16G 内存环境下分别模拟 3 次取平均值测得):
从上述对比可发现,最新支持 Spring Boot 3.0 基于 GraalVM 的 Spring Cloud Alibaba 应用会在启动速度、运行时内存占用和应用包大小方面得到大幅度降低,
例如,其中服务注册消费应用启动速度提升了近 10 倍,运行时内存占用比原来降低了近乎 2/3,效果非常明显。
这给云原生时代,托管在云上的应用带来了显著优势,让其可以更快地进行弹性扩缩容以及降低企业整体用云成本!
Spring Boot3在GraalVM上测试结果
参考:https://medium.com/deno-the-complete-reference/performance-benefits-brought-in-by-graalvm-springboot-hello-world-case-54d1a409a829
GraalVM 替代 JVM 确实可以带来巨大的性能优势。
GraalVM通过将Java应用程序编译成紧凑的独立二进制文件,实现了启动速度比传统Java应用程序快近100倍,无需预热即可提供峰值性能,同时显著减少了内存和CPU资源的消耗。这种技术彻底改变了Java应用程序的运行方式,提供了明显的优势。此外,GraalVM还支持多种编程语言,包括Java、Kotlin、Scala等,同时也支持JavaScript、Ruby、Python等其他语言,这种多语言支持的能力使得GraalVM成为一个强大的跨语言运行平台。
GraalVM的性能优势不仅体现在其运行Java程序的能力上,还在于其能够显著提高应用程序的启动速度和减少资源消耗。与传统的JVM相比,GraalVM通过其Native Image技术,可以将Java应用程序编译成独立的可执行文件,这种编译过程优化了代码的执行效率,减少了运行时的动态加载和解释执行,从而提高了应用程序的性能。
此外,GraalVM还提供了生产就绪功能,如指标、运行状况检查和外部化配置等,确保了应用程序的稳健性和可扩展性。这些功能使得GraalVM成为了一种理想的替代方案,尤其适用于对性能有较高要求的应用场景。
综上所述,GraalVM通过其独特的编译技术和对多种编程语言的支持,确实为Java应用程序带来了巨大的性能优势,使其成为JVM的一个有力替代选项
Spring Boot有助于轻松开发独立的、可用于生产的 Spring 应用程序。它对 Spring 平台和第三方库采用固执己见的方法:以最少的配置简化设置过程。优势:
-
易于使用:Spring Boot 简化了独立 Spring 应用程序的创建,无需复杂的配置。
-
嵌入式服务器:它允许直接嵌入 Tomcat、Jetty 或 Undertow 等服务器,从而无需单独部署 WAR 文件。
-
Starter 依赖项:Spring Boot 提供预配置的“starter”依赖项,降低了构建配置的复杂性。
-
自动配置:Spring Boot 自动配置 Spring 和第三方库,最大限度地减少手动设置工作。
-
生产就绪功能:它提供生产就绪功能,例如指标、运行状况检查和外部化配置,确保应用程序稳健且可扩展。
-
无需代码生成或 XML 配置:Spring Boot 运行时无需生成任何代码,无需 XML 配置文件,从而提高了开发效率。
在常见的部署中,用 Java 编写的 Spring Boot 应用程序被编译成默认在 Java 虚拟机 (JVM) 中运行的字节码。还有另一种鲜为人知的运行 Java 应用程序的方式:Native application
GraalVM通过提前将 Java 应用程序编译成紧凑的独立二进制文件,彻底改变了 Java 应用程序。这些二进制文件展现出明显的优势,启动速度比传统 Java 应用程序快近 100 倍。它们无需预热即可提供峰值性能,同时与 Java 虚拟机 (JVM) 同类产品相比,消耗的内存和 CPU 资源显着减少。
GraalVM 并不局限于理论创新领域;它受到 Spring Boot、Micronaut、Helidon 和 Quarkus 等主要微服务框架的支持。此外,Oracle Cloud Infrastructure、Amazon Web Services、Google Cloud Platform 和 Microsoft Azure 等领先的云平台完全支持 GraalVM 集成。
通过利用配置文件引导的优化和先进的 G1(垃圾优先)垃圾收集器,GraalVM 使我们的应用程序具有更低的延迟。事实上,它提供的性能指标与在 Java 虚拟机 (JVM) 上运行的应用程序的性能指标相当或更强。这种速度、效率和安全性的卓越结合使 GraalVM 成为现代 Java 开发的改变游戏规则的选择。
过去,有很多使用 GraalVM 对 Java 应用程序进行基准测试的请求,期望 GraalVM 能够超越传统的 Java 虚拟机 (JVM)。
在本篇文章中,我们将对各种 Java 应用程序的性能进行比较分析,评估它们在 JVM 和 GraalVM 环境中的执行情况。
我们将通过在 JVM(Java 虚拟机)和 GraalVM 上执行基本的“hello world”应用程序进行比较分析。通过这个比较,我们旨在探索 GraalVM 相对于传统 JVM 的优越性能。
测试设置
所有测试均在具有 16G RAM 的 MacBook M1 上执行。软件版本有:
-
JDK 21
-
Graalvm JDK 21
-
SpringBoot 3.1.4
应用程序代码是一个包含单个路由的简单文件:
package com.example.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.http.ResponseEntity;import org.springframework.http.HttpStatus;import org.springframework.web.bind.annotation.RestController;@SpringBootApplication@RestControllerpublic class DemoApplication { public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
} @GetMapping("/")
public String handleRequest() { return "Hello World!";
}
}
<?xml version="1.0" encoding="UTF-8"?><project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.4</version>
<relativePath/>
<!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.example.demo.DemoApplication</mainClass>
<layout>JAR</layout>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.9.27</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
<execution>
<id>test-native</id>
<goals>
<goal>test</goal>
</goals>
<phase>test</phase>
</execution>
</executions>
<configuration>
<!-- ... -->
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles></project>
结果
每个包含500万个请求的测试分别针对50、100和300个并发连接执行。对于负载测试,我们使用了 Bombardier 测试工具。
统计
为了更直观的展示测试结果,我们使用以下公式从结果中生成记分卡。对于每一个测量,结果获胜的按照领先度得分:
-
<5%,不给分
-
5%到20%之间,获胜者得1分
-
在20%到50%之间,获胜者得2分
-
>50%,获胜者得3分
结论
选择一个简单的 hello world 案例可能不是真正释放 GraalVM 或本机代码编译潜力的最合适场景。本地运行的相同 SpringBoot 应用程序的性能并没有明显优于其 JVM 对应项。GraalVM 唯一显着的优势在于其对内存的高效利用。
本文仅从性能方面对 GraalVM 和 传统 JVM 做了比较,参考以上测试结果,如果我们想要优化程序启动速度和对内存的利用率方面,GraalVM 会是更好的选择,至于其他性能指标,优势并不明显!
随着 GraalVM 在国内的推广和应用越来越广泛,相信它将会在未来的软件开发领域发挥越来越重要的作用,我们期待它之后的表现!