概述
在开发Spring Boot
应用或者其他JAVA
程序的过程中,启动慢、内存占用大是比较头疼的问题,往往需要更多的资源去部署,成本大幅提高。为了优化上述问题,常常使用优化程序、使用更小消耗的JVM、使用容器等措施。
现在有一个叫做Native Image
(原生镜像)的技术,可以将JAVA
应用的字节码直接编译为本地机器码,打包成本地可执行文件,运行应用时无需Java
虚拟机进行动态编译,因此启动速度很快、内存消耗也很低。
JIT & AOT
通常程序有两种运行方式:
- 动态解释:解释执行,运行时翻译为机器码。
- 静态编译:程序在执行前全部被翻译为机器码,可以直接运行二进制文件。
JIT (动态编译)
JIT
是Just-in-time
的缩写,一般称为即时编译或动态编译。Java
源代码在运行的过程中,类加载器将需要运行的字节码处理并分配到内存中,然后JVM
执行引擎需要调用解释器将字节码翻译为计算机能执行的机器码,最后执行机器指令。
需要解释执行势必会造成运行效率降低,为了提高执行速度,JAVA
引入了 JIT
编译器。当某个方法或代码块运行特别频繁的时候,JVM
会将其标注为热点代码, JIT
编译器会将热点代码编译成本地机器相关的机器码,优化后进行缓存,下次执行这些代码时,直接调用缓存中的机器码,无需重复解释,在一定程度上提高了执行速度。
AOT(静态编译)
JAVA
一直在努力提高启动和运行时性能,希望其能够在更广泛的场景达到或接近本地语言的性能。虽然引入了JIT
编译器,但是需要花费较长时间才能热身完,而且有些Java
方法还没法编译,性能方面也会下降。
AOT
是Ahead-of-Time
的缩写,一般称为静态编译。程序在执行前全部被翻译为机器码,可以直接运行二进制文件,比如C++
就是使用静态编译。
在 JDK 9
中, AOT
作为实验特性被引入,只支持 java.base
模块可以编译成AOT
库,使用jaotc
工具将Java
类文件编译为本机代码,避免了 JIT
预热等各方面的开销。但是实际运行效果不尽人意,最终在JDK 16
中被删除。
在JDK 17
中,也移除了实验性的AOT
与JIT
,彻底拥抱GraalVM
实现静态编译。
在当前微服务、云原生盛行的时代,JAVA 程序
显得越来越臃肿,虽然使用AOT
也有诸多缺点,比如打包时间长、舍弃平台无关性、反射、JNI、动态代理的分析能力有限。但是JAVA
必定会向AOT
发展,否则在云原生时代,可以能被其他后起之秀慢慢蚕食市场。
GraalVM
简介
官网地址
GitHub地址
GraalVM
是一个高性能跨语言虚拟机,其目的是提升Java
和其他JVM
语言编写程序的执行速度,同时也为JavaScript
、Python
和许多其他流行语言提供运行时环境。起始于 2011 年 Oracle
实验室的一个研究项目。
GraalVM
三大核心:
- 为
Java
虚拟机提供高性能的JIT
编译器 - 高性能的
AOT
编译器,提前将Java
字节码编译为本机机器码。 - 多种语言的支持,
GraalVM
的Truffle
语言实施框架可与GraalVM
编译器协作,以卓越性能运行JavaScript、Python、Ruby
以及JVM
支持的其他语言。
GraalVM
提供了两种运行Java
应用程序的方法:
- 在
HotSpot JVM
上使用实时JIT
编译器 - 使用
AOT
将Java
应用程序编译的本地可执行文件
GraalVM
的优点:
-
帮助开发人员显著提升应用的性能效率,同时降低
IT
成本。 -
构建现代
Java
应用,通过微服务和容器来满足云原生需求,微服务是执行单一功能的小型、独立微应用。在现实中,业务应用通常要使用数百项服务,每项服务都需要快速启动,以尽可能降低延迟和云使用成本。 -
可以构建一个各种编程语言基于单一
JVM
运行的生态系统,提高开发效率。
GraalVM
的缺点:
- 舍弃了
Java
的平台无关性,编译为本地执行文件,不同操作系统的服务器,编译出来的文件不一样,比如Windows
编译出来的文件,并不能在Linux
系统运行,也就让JAVA
丢失了平台无关性。JAVA
设计之初,一次编译、到处运行是其最重要的特性,但是现在容器技术的出现,该特性显得很牵强。 - 反射机制、
CGLIB
动态代理这些和字节码打交道的机制,是在程序运行时动态调用,无法经过AOT
编译成原生代码,构建时还需要提供各种配置文件去适配。 - 目前该技术并未大面积使用,并不成熟。
运行模式
GraalVM
提供了多种操作模式。
1、JVM运行时模式
在HotSpot JVM
上运行程序时,默认使用GraalVM
编译器作为顶级JIT
编译器。在运行时,应用程序在JVM上
正常加载和执行。JVM
将字节码传递给编译器,编译器将其编译为机器代码并将其返回给JVM
。
2、原生镜像
Native Image
是一种创新技术,它将Java
代码编译为独立的本地可执行文件或本地共享库。在构建本机可执行文件期间,处理的Java
字节码包括所有应用程序类、依赖项、依赖于第三方的库以及所需的任何JDK类。生成的本地可执行文件特定于每个操作系统和机器体系结构,并不需要JVM
。
3、Java on Truffle
Java on Truffle
是一个 JVM
实现,它使用了 Truffle
多语言执行框架。提供了Java
虚拟机所有的核心组件,实现了与Java
运行时环境库相同的API
,并重用GraalVM中
的所有JAR
和本机库。支持多语言互操作,例如,JavaScript
程序可以运行Ruby
方法,无需制作副本就能共享数值。基于JVM
运行时,Truffle
能够与GraalVM
编译器协作,将受支持语言编译为本机机器码,从而优化性能。
Native Image(原生镜像)
了解了GraalVM
之后,我们着重了解并使用Native Image
技术将一个Spring Boot 3
项目编译为一个可执行二进制文件。
Native Image
:是一种将Java
代码提前编译为二进制文件的技术,即本机可执行文件。本机可执行文件只包含运行时所需的代码,即应用程序类、标准库类、语言运行时以及来自JDK的静态链接本机代码。
Native Image
处理应用程序类和其他元数据,以创建特定操作系统和体系结构的二进制文件。首先,本地镜像工具对代码执行静态分析,以确定应用程序运行时可访问的类和方法。其次,它将类、方法和资源编译成二进制文件。整个过程被称为构建,以明确区分它与Java
源代码到字节码的编译。
Native Image
生成的可执行文件优点:
- 使用虚拟机所需资源的一小部分,运行时更低的内存消耗
- 以毫秒为单位启动
- 立即提供最高性能,无需预热
- 可以打包成轻量级容器映像,以实现快速高效的部署
- 更不容易遭到破解、攻击
Spring Native
Spring Native
是Spring
社区的一个开源框架,可以通过GraalVM
将Spring
应用程序编译成原生镜像。
官方推荐使用Spring Boot 3
+GraalVM
官方构建工具实现原生镜像构建,所以要注意Spring Native
已经没必要再去研究及使用了
graalvm安装并使用native-image
下载GraalVM安装包
1 进入GitHub 下载地址
3 配置环境变量
WIN键 搜索环境变量,即可打开。
如果之前配置过java的环境变量 可以直接替换掉JAVA_HOME
如果没有配置过,需要配置JAVA_HOME,并在系统path中添加 %JAVA_HOME%\bin
点击“确定”按钮保存更改。
tips: 刷新变量 WIN+R refreshenv
4 验证安装
如果输出的结果包含GraalVM的版本信息,则说明安装成功。
已经成功安装和配置了GraalVM在Windows上,可以开始使用它进行Java开发或者运行其他语言的程序。接下来需要安装native-image。
安装native-image
Native Image
:是一种将Java
代码提前编译为二进制文件的技术,即本机可执行文件。
gu.cmd install native-image
可以使用命令查看,已经安装的功能
gu.cmd list
测试
创建一个 HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello native image, World!");
}
}
将HelloWorld.java编译为字节码,然后构建本机可执行文件:
javac HelloWorld.java
native-image HelloWorld
报错:要安装 Visual Studio 2022
安装 Visual Studio 2022
链接:
最后一个命令生成在当前工作目录中命名的可执行文件。 调用它将执行类的本机编译代码,如下所示:Hello native image, World!
成功!