Spring Boot 3.0系列【2】部署篇之使用GraalVM构建原生镜像
有道无术,术尚可求,有术无道,止于术。
本系列Spring Boot版本2.7.0
文章目录
- 概述
- JIT & AOT
- JIT (动态编译)
- AOT(静态编译)
- GraalVM
- 简介
- 运行模式
- Native Image(原生镜像)
- Spring Native
- 案例演示
- 1. 安装 GraalVM Native-image C++环境
- 2. 项目测试
概述
本篇介绍如何使用GraalVM构建原生镜像。
在开发Spring Boot 应用或者其他JAVA程序的过程中,启动慢、内存占用大是比较头疼的问题,往往需要更多的资源去部署,成本大幅提高。为了优化上述问题,常常使用优化程序、使用更小消耗的JVM、使用容器等措施。
现在有一个叫做Native Image(原生镜像)的技术,可以将JAVA应用的字节码直接编译为本地机器码,打包成本地可执行文件,运行应用时无需Java虚拟机进行动态编译,因此启动速度很快、内存消耗也很低。
JIT & AOT
在正式介绍Native Image之前,我们需要了解下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应用程序编译的本地可执行文件
社区版和企业版的一些区别:
- 社区版基于
Open JDK,由社区组织维护 - 社区版无定制的高级编译器优化
- 在原生镜像中,社区版无压缩指针、配置文件引导优化、G1垃圾收集

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。
从当前GraalVM 22.1支持的功能图可以看出,Native Image可以在AMD6、AArch64等服务器平台生成环境使用。

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应用程序编译成原生镜像。
但是在GitHub可以看到,该项目已经关闭了:

官方推荐使用Spring Boot 3+GraalVM官方构建工具实现原生镜像构建,所以要注意Spring Native已经没必要再去研究及使用了
案例演示
官方环境要求文档
首先需要安装GraalVM、native-image组件,C++环境,最好不要在Windows 上使用,可以看到Spring Boot官网给出Windows 上使用的注意事项,就算按步骤做了~ 也有可能各种报错,所以切勿尝试😁😁😁

1. 安装 GraalVM Native-image C++环境
下载地址
选择系统对应的GraalVM、Native-image安装包并下载,这里我使用的是Linux Ubuntu系统。


将文件上传到Linux 服务器:

执行以下命令安装GraalVM
# 解压tar -zxvf graalvm-ce-java17-linux-amd64-22.3.1.tar.gz
# 添加环境变量
vim /etc/profile
# 添加内容
JAVA_HOME=/root/graalvm-ce-java17-22.3.1/
CLASSPATH=$JAVA_HOME/lib/
PATH=$PATH:$JAVA_HOME/bin
export PATH JAVA_HOME CLASSPATH
# 环境变量生效
source /etc/profile
# 查看
java -version

执行以下命令安装native-image
# 安装
gu -L install native-image-installable-svm-java17-linux-amd64-22.3.1.jar
# 查看
gu list

执行以下命令安装C++环境:
# Ubuntu 系统
sudo apt-get install build-essential libz-dev zlib1g-dev
2. 项目测试
准备一个Spring Boot 3.0.3项目:

添加GraalVM官方提供的构建插件:
<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><!--原生镜像构建插件--><plugin><groupId>org.graalvm.buildtools</groupId><artifactId>native-maven-plugin</artifactId><version>0.9.20</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><mainClass>com.pearl.nativeimagedemo.NativeImageDemoApplication</mainClass><imageName>native-image-demo</imageName><buildArgs><buildArg>--verbose</buildArg></buildArgs></configuration></plugin></plugins></build>
在Linux服务器上安装Maven,将项目代码上传到服务器(内存至少6G,太少会卡住),执行mvn -Pnative -DskipTests native:compile编译,时间还是蛮久的,虽然这个项目比较空。

编译后在target下生成可执行文件,直接使用./native-image-demo就可运行,可以看到启动时间只有几十毫秒:

相关文章:
Spring Boot 3.0系列【2】部署篇之使用GraalVM构建原生镜像
有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot版本2.7.0 文章目录概述JIT & AOTJIT (动态编译)AOT(静态编译)GraalVM简介运行模式Native Image(原生镜像)…...
复习知识点十之方法的重载
目录 方法的重载 练习1: 练习1: 数组遍历 练习2: 数组的最大值 练习3: 练习4: 复制数组 基本数据类型和引用数据类型 方法的重载 Java虚拟机会通过参数的不同来区分同名的方法 练习1: public class Test4 {public static void main(String[] args) {//调用方法 // …...
火爆全网的ChatGPT 和AI 可以为项目经理做什么?
作为一款人工智能聊天机器人,ChatGPT因其逼真和人性化的特性而风靡全球,无疑是当今技术的新流行。人工智能 (AI) 有可能彻底改变许多行业,包括项目管理,及时了解最新技术以及它如何影响你的工作至关重要。于是,我们与C…...
前端面试题 —— HTML
目录 一、src 和 href 的区别 二、对 HTML 语义化的理解 三、DOCTYPE(⽂档类型) 的作⽤ 四、script 标签中 defer 和 async 的区别 五、常⽤的 meta 标签有哪些? 六、HTML5 有哪些更新 八、行内元素有哪些?块级元素有哪些? 空(void)元素…...
同为(TOWE)电源线让家用电器随心放置
如今,随着科技水平的不断发展,人们工作、生活中越来越离不开各类电子设备和电器产品。当用电器数量多了以后,由于电器设备原有电线长度的限制,常常需要通过连接接线板来延长电器设备的电能传输线路。电源线虽然看着是一件不起眼的…...
2023上半年数学建模竞赛汇总(报名时间、比赛时间、难易程度、含金量、竞赛官网)
1、美国大学生数学建模竞赛等级:国家级是否可跨校:否竞赛开始时间:2月17日~2月21日综合难度:⭐⭐⭐⭐ 竞赛含金量:⭐⭐⭐⭐⭐竞赛官网:https://www.comap.com/2、MathorCup高校数学建模挑战赛---大数据竞赛…...
RK3568平台开发系列讲解(驱动基础篇)SMP(Symmetrical Multi-Processing)
🚀返回专栏总目录 文章目录 一、linux SMP 和 AMP二、linux SMP的启动流程三、CPU的描述:cpumask四、CPU之间的关系沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 SMP(Symmetrical Multi-Processing)。 一、linux SMP 和 AMP 目前支持多核处理器的实时操…...
HIVE --- zeppelin安装
目录 把zeppelin压缩包拷贝到虚拟机里面 解压 改名 修改配置文件 编辑zeppelin-site.xml—将配置文件的ip地址和端口号进行修改 编辑 zeppelin-env.sh—添加JDK和Hadoop环境 配置环境变量 刷新环境变量 拷贝Hive文件 拷贝外部文件 启动zeppelin 启动Hadoop&Hi…...
数据分析中的变量解释
1.数值变量Numerical Variables 数值型变量(metric variable)是说明事物数字特征的一个名称,其取值是数值型数据。如“产品产量”、“商品销售额”、“零件尺寸”、“年龄”、“时间”等都是数值型变量,这些变量可以取不同的数值…...
django-博客(一)
一、 1、环境:pycharm,python3.6,django3,mysql8.0 2、创建项目 3、把html和css样式那些导入到文件夹中,然后配置这些文件夹的路径,再添加首页视图。 改成反向解析 python manage.py runserv…...
Shell高级——Linux中的文件描述符
以下内容源于C语言中文网的学习与整理,非原创,如有侵权请告知删除。 前言 Linux中一切接文件,比如 C 源文件、视频文件、Shell脚本、可执行文件等,就连键盘、显示器、鼠标等硬件设备也都是文件。 一个 Linux 进程可以打开成百上…...
洗地机哪个品牌最好用?家用洗地机十大名牌
这几年清洁类的小家电非常热门,无线吸尘器、扫地机器人、扫拖一体机、洗地机和擦窗机器人层出不穷,各个品牌百花齐放。这些清洁电器,确实为家庭卫生清洁带来了很大的便捷。但要把这些产品一次性买齐是一笔不小的开销,而且需要收纳…...
java多线程(十)线程休眠
一、sleep()介绍 sleep() 定义在Thread.java中。 sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由…...
Leetcode20. 有效的括号
一、题目描述: 给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。左括号必须以正确…...
Android 项目必备(四十三)-->Android 开发者的 new 电脑
前言 作为 Android 开发者,当你新入职一家公司,拿到新发的电脑,你会对电脑干点啥? 安装开发环境?装软件?你是否还会铺天盖地到处找之前电脑备份的东西?又或者还想不起来有什么上一台电脑好用的…...
如何水平和垂直居中元素
跳到主内容 我试图将我的选项卡内容垂直居中,但是当我添加 CSS 样式时display:inline-flex,水平文本对齐消失了。 如何为每个选项卡同时对齐文本 x 和 y? * { box-sizing: border-box; } #leftFrame {background-color: green;position: a…...
Rust泛型Generics
泛型 泛型(Generics)是一种程序设计风格,它允许程序员在强类型语言(例如rust,c#,c)中编写代码时使用通用类型。以rust为例,如果你想实现一个通用的add函数,让其在u8, i3…...
六、并发集合
文章目录并发集合ConcurrentHashMap存储结构存储操作put方法putVal方法-散列算法putVal方法-添加数据到数组&初始化数组putVal方法-添加数据到链表扩容操作treeifyBin方法触发扩容tryPreSize方法-针对putAll的初始化操作tryPreSize方法-计算扩容戳并且查看BUGtryPreSize方法…...
PHY调试经验
1. PHY调试过程 1.设备树中配置正确的PHY ADDR、PHY ID、clause 45或者22协议,PHY ADDR配置不正确会导致MDC/MDIO通信不正常或失败,PHY ID用于匹配PHY驱动程序。 2.通过MDC/MDIO读写PHY ID并对比datasheet中的PHY ID,确认MDC/MDIO通信是否正常…...
从Java培训班出来好找工作吗?
个人觉得这个问题要从两方面来看,首先是培训班的Java课程质量如何,是否贴合用人单位实际需求,学出来的技术能对口;其次是培训班是否保障就业,有就业机会渠道推荐,比如老学员内推、合作企业人才输送以及企业…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
