本文示例均采用Java11,GraalVM目前无法支持跨平台编译,比如,我通过Linux直接编译Windows可执行的exe,是不行的。
因此,需要掌握两种平台的GraalVM的安装、使用。
一、背景
1.1 为何GraalVM快?
常规Java编译的Jar,为了考虑平台的兼容性,在启动后,将字节码解释为机器码,这个过程叫做JIT编译。所以启动上会慢。
GraalVM编译的二进制程序,在编译时直接将字节码转为了机器码,这个过程叫做AOT编译。所以启动上会快,但是对反射支持的就不友好。
因此Java严格来说既是编译型,又是解释型。只不过编译成的是字节码。
1.2 JIT与AOT对比
JIT(Just-in-Time)编译和AOT(Ahead-of-Time)编译是两种不同的编译策略,它们在代码编译和执行的时间点上有所区别。
- JIT(Just-in-Time)编译:
- JIT 编译是在程序运行时动态地将字节码或中间代码(如Java字节码)转换为本机机器码。
- JIT 编译器根据程序的运行时行为和环境进行优化,并将热点代码(频繁执行的代码)即时编译为本机机器码。
- JIT 编译器可以根据特定硬件平台的特性进行优化,以提高执行速度和性能。
- JIT 编译通常用于解释性语言或虚拟机中,例如Java虚拟机(JVM)。
- AOT(Ahead-of-Time)编译:
- AOT 编译是在程序运行之前将源代码或中间代码(如字节码)静态编译为本机机器码。
- AOT 编译器在编译阶段进行优化,根据目标平台的特性生成高效的本机代码。
- AOT 编译可以提供更快的启动时间和更稳定的性能,因为代码已经被编译为本机机器码,无需再进行即时编译。
- AOT 编译通常用于静态语言或需要更高性能的应用程序,例如C/C++编译器。
主要区别:
- JIT 编译是在运行时动态地将代码编译为机器码,而 AOT 编译是在程序运行之前将代码静态编译为机器码。
- JIT 编译可以根据运行时行为进行优化,而 AOT 编译在编译阶段进行优化。
- JIT 编译需要在程序运行时进行编译,可能导致启动延迟和即时编译的开销,而 AOT 编译可以提供更快的启动时间和更稳定的性能。
需要注意的是,JIT 编译和 AOT 编译并不是互斥的概念,而是两种不同的编译策略。在某些情况下,可以结合使用它们,例如GraalVM提供了同时支持 JIT 编译和 AOT 编译的能力,使得可以根据应用程序的需求选择最适合的编译方式。
二、Linux
2.1 前置环境
环境:Centos7.9、科学上网
yum -y install gcc zlib*
2.2 安装GraalVM-ce-java11
使用 uname
命令:
uname -m
该命令将返回当前系统的机器架构信息。如果返回值是 “x86_64”,则表示系统是 AMD64 架构;如果返回值是 “aarch64”,则表示系统是 AArch64 架构。
curl -L -o graalvm-ce-java11-linux-amd64-21.3.3.1.tar.gz https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.3.3.1/graalvm-ce-java11-linux-amd64-21.3.3.1.tar.gz && tar -zxvf graalvm-ce-java11-linux-amd64-21.3.3.1.tar.gz && cd graalvm-ce-java11-21.3.3.1/bin && ./gu install native-image && ./gu list
执行如图
三、Windows
3.1 前置环境
windows需要预置的C++环境,这边可以自己下,也可以直接使用Visual Studio下载。我的电脑是win11,而且刚好最近编写dll,因此直接使用Visual Studio2022来安装环境。
打开Visual Studio2022-工具-获取工具和功能
3.2 安装GraalVM-ce-java11
略。没啥可说的,按照Linux的步骤,在Windows配一下即可。
四、编译二进制程序
源码地址
4.1 编译字节码
创建Test.java
import java.text.SimpleDateFormat;
import java.util.Date;
class Test {
public static void main(String[] args) {
System.out.println("hello world! now time is "+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
编译Test.java
./javac Test.java
# 执行下面这行代码,会发现输出hello world! now time is 2023-05-16 21:10:09
./java Test
# 编译为二进制程序
./native-image Test
如图即为编译成功
移到没有Java环境的路径,执行二进制程序
mv test /root/test && cd && chmod +x test && ./test
4.2 编译jar包
4.2.1 编译原生jar包
执行命令
mvn clean package
native-image -jar pure-demo.jar
在native-image中,即使主线程结束,其他线程一样执行,程序不会退出
4.2.2 编译springboot jar包
保持原来的代码不变基础上,需要引入spring-native和graalvm打包插件
<dependencies>
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>0.9.2</version>
<executions>
<execution>
<id>test-generate</id>
<goals>
<goal>test-generate</goal>
</goals>
</execution>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native-image</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>21.0.0.2</version>
<configuration>
<!-- native-image需要执行的应用程序的入口点 -->
<mainClass>${start.class}</mainClass>
<buildArgs>
<buildArg>--enable-https</buildArg>
</buildArgs>
</configuration>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
执行命令,直接打包成镜像
mvn clean package -Pnative-image
放一张原生jar的运行耗时。
GraalVM牛逼!
五、参考致谢
-
GraalVM Native Image: Hello World - YouTube
-
官方文档
-
Spring Tips: The GraalVM Native Image Builder Feature
-
参考第3条的视频:将SpringBoot2.3+jdk8应用编译为二进制
-
Spring Tips: The GraalVM Native Image Builder Feature
-
参考第3条的视频:将SpringBoot2.3+jdk8应用编译为二进制