当前位置: 首页 > news >正文

Spring Boot3,启动时间缩短 10 倍!

前面松哥写了一篇文章和大家聊了 Spring6 中引入的新玩意 AOT(见Spring Boot3 新玩法,AOT 优化!)。

文章发出来之后,有小伙伴问松哥有没有做性能比较,老实说,这个给落下了,所以今天再来一篇文章,和小伙伴们梳理比较小当我们利用 Native Image 的时候,Spring Boot 启动性能从参数上来说,到底提升了多少。

先告诉大家结论:启动速度提升 10 倍以上。

1. Native Image

1.1 GraalVM

不知道小伙伴们有没有注意到,现在当我们新建一个 Spring Boot 工程的时候,再添加依赖的时候有一个 GraalVM Native Support,这个就是指提供了 GraalVM 的支持。

那么什么是 GraalVM 呢?

GraalVM 是一种高性能的通用虚拟机,它为 Java 应用提供 AOT 编译和二进制打包能力,基于 GraalVM 打出的二进制包可以实现快速启动、具有超高性能、无需预热时间、同时需要非常少的资源消耗,所以你把 GraalVM 当作 JVM 来用,是没有问题的。

在运行上,GraalVM 同时支持 JIT 和 AOT 两种模式:

  • JIT 是即时编译(Just-In-Time Compilation)的缩写。它是一种在程序运行时将代码动态编译成机器码的技术。与传统的静态编译(Ahead-of-Time Compilation)不同,静态编译是在程序执行之前将代码编译成机器码,而 JIT 编译器在程序运行时根据需要将代码片段编译成机器码,然后再运行。所以 JIT 的启动会比较慢,因为编译需要占用运行时资源。我们平时使用 Oracle 提供的 Hotspot JVM 就属于这种。

  • AOT 是预先编译(Ahead-of-Time Compilation)的缩写。它是一种在程序执行之前将代码静态编译成机器码的技术。与即时编译(JIT)不同,即时编译是在程序运行时动态地将代码编译成机器码。AOT 编译器在程序构建或安装阶段将代码转换为机器码,然后在运行时直接执行机器码,而无需再进行编译过程。这种静态编译的方式可以提高程序的启动速度和执行效率,但也会增加构建和安装的时间和复杂性。AOT 编译器通常用于静态语言的编译过程,如 C、C++ 等。

如果我们在 Java 应用程序中使用了 AOT 技术,那么我们的 Java 项目就会被直接编译为机器码可以脱离 JVM 运行,运行效率也会得到很大的提升。

那么什么又是 Native Image 呢?

1.2 Native Image

Native Image 则是 GraalVM 提供的一个非常具有特色的打包技术,这种打包方式可以将应用程序打包为一个可脱离 JVM 在本地操作系统上独立运行的二进制包,这样就省去了 JVM 加载和字节码运行期预热的时间,提升了程序的运行效率。

Native Image 具备以下特点:

  • 即时启动:由于不需要 JVM 启动和类加载过程,Native Image 可以实现快速启动和即时执行。
  • 减少内存占用:编译成本地代码后,应用程序通常会有更低的运行时内存占用,因为它们不需要 JVM 的额外内存开销。
  • 静态分析:在构建 Native Image 时,GraalVM 使用静态分析来确定应用程序的哪些部分是必需的,并且只包含这些部分,这有助于减小最终可执行文件的大小。
  • 即时性能:虽然 JVM 可以通过JIT(Just-In-Time)编译在运行时优化代码,但 Native Image 提供了即时的、预先优化的性能,这对于需要快速响应的应用程序特别有用。
  • 跨平台兼容性:Native Image 可以为不同的操作系统构建特定的可执行文件,包括 Linux、macOS 和 Windows,即在 Mac 和 Linux 上自动生成系统可以执行的二进制文件,在 Windows 上则自动生成 exe 文件。
  • 安全性:由于 Native Image 不依赖于 JVM,因此减少了 JVM 可能存在的安全漏洞的攻击面。
  • 与 C 语言互操作:Native Image 可以与本地 C 语言库更容易地集成,因为它们都是在同一环境中运行的本地代码。

根据前面的介绍大家也能看到,GraalVM 所做的事情就是在程序运行之前,该编译的就编译好,这样当程序跑起来的时候,运行效率就会高,而这一切,就是利用 AOT 来实现的。

但是!对于一些涉及到动态访问的东西,GraalVM 似乎就有点力不从心了,原因很简单,GraalVM 在编译构建期间,会以 main 函数为入口,对我们的代码进行静态分析,静态分析的时候,一些无法触达的代码会被移除,而一些动态调用行为,例如反射、动态代理、动态属性、序列化、类延迟加载等,这些都需要程序真正跑起来才知道结果,这些就无法在编译构建期间被识别出来。

而反射、动态代理、序列化等恰恰是我们 Java 日常开发中最最重要的东西,不可能我们为了 Native Image 舍弃这些东西!因此,从 Spring6(Spring Boot3)开始支持 AOT Processing!AOT Processing 用来完成自动化的 Metadata 采集,这个采集主要就是解决反射、动态代理、动态属性、条件注解动态计算等问题,在编译构建期间自动采集相关的元数据信息并生成配置文件,然后将 Metadata 提供给 AOT 编译器使用。

道理搞明白之后,接下来通过一个案例来感受下 Native Image 的威力吧!

2. 准备工作

首先需要我们安装 GraalVM。

GraalVM 下载地址:

  • https://www.graalvm.org/downloads/

下载下来之后就是一个压缩文件,解压,然后配置一下环境变量就可以了,这个默认大家都会,我就不多说了。

GraalVM 配置好之后,还需要安装 Native Image 工具,命令如下:

gu install native-image

装好之后,可以通过如下命令检查安装结果:

另一方面,Native Image 在进行打包的时候,会用到一些 C/C++ 相关的工具,所以还需要在电脑上安装 Visual Studio 2022,这个我们安装社区版就行了(https://visualstudio.microsoft.com/zh-hans/downloads/):

下载后双击安装就行了,安装的时候选择 C++ 桌面应用开发。

如此之后,准备工作就算完成了。

3. 实践

接下来我们创建一个 Spring Boot 工程,并且引入如下两个依赖:

然后我们开发一个接口:

@RestController
public class HelloController {@AutowiredHelloService helloService;@GetMapping("/hello")public String hello() {return helloService.sayHello();}
}
@Service
public class HelloService {public String sayHello() {return "hello aot";}
}

这是一个很简单的接口,接下来我们分别打包成传统的 jar 和 Native Image。

传统 jar 包就不用我多说了,大家执行 mvn package 即可:

mvn package

打包完成之后,我们看下耗时时间:

耗时不算很久,差不多 3.7s 左右,算是比较快了,最终打成的 jar 包大小是 18.9MB。

再来看打成原生包,执行如下命令:

mvn clean native:compile -Pnative

这个打包时间就比较久了,需要耐心等待一会:

可以看到,总共耗时 4 分 54 秒。

Native Image 打包的时候,如果我们是在 Windows 上,会自动打包成 exe 文件,如果是 Mac/Linux,则生成对应系统的可执行文件。

这里生成的 aot_demo.exe 文件大小是 82MB。

两种不同的打包方式,所耗费的时间完全不在一个量级。

再来看启动时间。

先看 jar 包启动时间:

耗时约 1.326s。

再来看 exe 文件的启动时间:

好家伙,只有 0.079s。

1.326/0.079=16.78

启动效率提升了 16.78 倍!

我画个表格对比一下这两种打包方式:

jarNative Image
包大小18.9MB82MB
编译时间3.7s4分54s
启动时间1.326s0.079s

从这张表格中我们可以看到,Native Image 在打包的时候比较费时间,但是一旦打包成功,项目运行效率是非常高的。Native Image 很好的解决了 Java 冷启动耗时长、Java 应用需要预热等问题。

最后大家可以自行查看打包成 Native Image 时候的编译结果,如下图:



看过松哥之前将的 Spring 源码分析的小伙伴,这块的代码应该都很好明白,这就是直接把 BeanDefinition 给解析出来了,不仅注册了当前 Bean,也把当前 Bean 所需要的依赖给注入了,将来 Spring 执行的时候就不用再去解析 BeanDefinition 了。

同时我们可以看到在 META-INF 中生成了 reflect、resource 等配置文件。这些是我们添加的 native-maven-plugin 插件所分析出来的反射以及资源等信息,也是 Spring AOT Processing 这个环节处理的结果。

相关文章:

Spring Boot3,启动时间缩短 10 倍!

前面松哥写了一篇文章和大家聊了 Spring6 中引入的新玩意 AOT(见Spring Boot3 新玩法,AOT 优化!)。 文章发出来之后,有小伙伴问松哥有没有做性能比较,老实说,这个给落下了,所以今天…...

Picturesocial | 只要 5 分钟,发现容器编排的秘密武器!

在上一篇文章《Picturesocial | 开发实践:如何在 15 分钟内将应用容器化》,我们讨论了容器以及容器化应用程序所需的步骤。在不考虑将 container 部署到哪里的情况下创建 container,就像把家放在漂浮在海中的货运集装箱里一样,听起…...

GEE数据集——Umbra 卫星合成孔径雷达开放数据

Umbra 合成孔径雷达开放数据 Umbra 卫星生成的合成孔径雷达图像分辨率最高(优于 25 厘米/10 英寸)。合成孔径雷达卫星可以在夜间、透过云层、烟雾和雨水捕捉图像。合成孔径雷达具有监测变化的独特能力。开放数据计划(ODP)对全球十个不同地点进行监测。经常更新新图像。ODP …...

一个vue项目中通过iframe嵌套另外一个vue项目,如何让这两个项目进行通信

文章目录 需求分析父传子子传父 需求 一个vue项目中通过iframe嵌套另外一个vue项目,如何让这两个项目之间进行通信 分析 在Vue项目中通过iframe嵌套另外一个Vue项目时,可以通过postMessage方法实现这两个项目之间的通信。postMessage是HTML5新增加的API…...

上班族学习方法系列文章目录

上班族学习方法系列文章目录 文章目录 上班族学习方法系列文章目录前言一、时间管理二、答题实战 前言 上班族如果想提高自己,那么就得掌握有效的学习方法和良好的时间管理。 一、时间管理 上班族有家有业,考证或者提高学历备考时间不充分。需要学会精…...

《Lua程序设计》-- 学习9

迭代器和泛型for 迭代器和闭包 迭代器(iterator)是一种可以让我们遍历一个集合中所有元素的代码结构。在Lua语言中,通常使用函数表示迭代器:每一次调用函数时,函数会返回集合中的“下一个”元素。 一个闭包就是一个…...

GIS应用水平考试一级—2009 年度第二次

全国信息化工程师——GIS应用水平考试 2009 年度第二次全国统一考试一级 试卷说明: 1、本试卷共9页,6个大题,满分150 分,150 分钟完卷。 2、考试方式为闭卷考试。 3、将第一、二、三題的答案用铅笔涂写到(NCIE-GIS)答题卡上。 4、将第四、五、六题的答案填写到主观题答题卡上…...

【计算机视觉】万字长文详解:卷积神经网络

以下部分文字资料整合于网络,本文仅供自己学习用! 一、计算机视觉概述 如果输入层和隐藏层和之前一样都是采用全连接网络,参数过多会导致过拟合问题,其次这么多的参数存储下来对计算机的内存要求也是很高的 解决这一问题&#x…...

Vue3项目封装一个Element-plus Pagination分页

前言:后台系统分页肯定是离不开的,但是ui框架都很多,我们可以定义封装一种格式,所有项目按到这个结构来做. 实例: 第一步:在项目components组件新建一个分页组件,用来进行封装组件. 第二步:根据官方的进行定义,官方提供的这些,需要我们封装成动态模式 第三步:代码改造 <!-…...

node.js(nest.js控制器)学习笔记

nest.js控制器&#xff1a; 控制器负责处理传入请求并向客户端返回响应。 为了创建基本控制器&#xff0c;我们使用类和装饰器。装饰器将类与所需的元数据相关联&#xff0c;并使 Nest 能够创建路由映射&#xff08;将请求绑定到相应的控制器&#xff09;。 1.获取get请求传参…...

Mybatis 源码系列:领略设计模式在 Mybatis 其中的应用

文章目录 一、Builder模式二、工厂模式三、单例模式四、代理模式五、组合模式六、模板方式模式七、适配器模式八、装饰器模式九、迭代器模式 虽然我们都知道有23种设计模式&#xff0c;但是大多停留在概念层面&#xff0c;真实开发中很少遇到&#xff0c;Mybatis源码中使用了大…...

用的到的linux-文件移动-Day2

前言&#xff1a; 在上一节&#xff0c;我们复习了cd大法和创建生成文件和文件夹的方法&#xff0c;介绍了一些“偷懒”&#xff08;高效&#xff09;的小技巧&#xff0c;本节&#xff0c;我们一起来探讨下&#xff0c;我们对文件移动操作时有哪些可以偷懒的小技巧~ 一、复制…...

红队打靶练习:INFOSEC PREP: OSCP

目录 信息收集 1、arp 2、nmap WEB 信息收集 wpscan dirsearch ssh登录 提权 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:69:c7:bf, IPv4: 192.168.110.128 Starting arp-scan 1.10.0 with 256 ho…...

【linux】文件修改记录

是的&#xff0c;在Linux上&#xff0c;您可以使用’ find 命令检查最近修改的文件。此实用程序可以搜索在指定天数内修改过的文件。你可以这样使用它: 查找主目录中最近24小时(1天)内修改过的文件。 find ~ -type f -mtime -1命令说明: -“~”表示您的主目录。 ’ -type f…...

Vue学习Element-ui

声明&#xff1a;本文来源于黑马程序员PDF讲义 Ajax 我们前端页面中的数据&#xff0c;如下图所示的表格中的学生信息&#xff0c;应该来自于后台&#xff0c;那么我们的后台和前端是 互不影响的2个程序&#xff0c;那么我们前端应该如何从后台获取数据呢&#xff1f;因为是2…...

存内计算技术—解决冯·诺依曼瓶颈的AI算力引擎

文章目录 存内计算技术背景CSDN首个存内计算开发者社区硅基光电子技术存内计算提升AI算力知存科技存算一体芯片技术基于存内计算的语音芯片的实现挑战 参考文献 存内计算技术背景 存内计算技术是一种革新性的计算架构&#xff0c;旨在克服传统冯诺依曼架构的瓶颈&#xff0c;并…...

数据结构--树

一、树的基本术语 结点:树中的一个独立单元 结点的度:结点下分支的个数 树的度:树中所有结点中度的最大值 非终端结点:度不为0的结点 双亲和孩子:结点下的子树称为该结点的孩子.相应地,该结点称为孩子的双亲 兄弟:同一个双亲的孩子之间 祖先:从根到该结点所经分支上的所…...

计算机网络_1.3电路交换、分组交换和报文交换

1.3电路交换、分组交换和报文交换 一、电路交换1、“电路交换”例子引入2、电路交换的三个阶段3、计算机之间的数据传送不适合采用电路交换 二、分组交换1、发送方&#xff08;1&#xff09;报文&#xff08;2&#xff09;分组&#xff08;3&#xff09;首部 2、交换节点3、接收…...

【AI视野·今日NLP 自然语言处理论文速览 第七十七期】Mon, 15 Jan 2024

AI视野今日CS.NLP 自然语言处理论文速览 Mon, 15 Jan 2024 Totally 57 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Machine Translation Models are Zero-Shot Detectors of Translation Direction Authors Michelle Wastl, Ja…...

神经网络的一些常规概念

epoch&#xff1a;是指所有样本数据在神经网络训练一次&#xff08;单次epoch(全部训练样本/batchsize)/iteration1&#xff09;或者&#xff08;1个epochiteration数 batchsize数&#xff09; batch-size&#xff1a;顾名思义就是批次大小&#xff0c;也就是一次训练选取的样…...

【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程

【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程 文章目录 前言一、引入依赖二、创建数据库连接简单链接连接选项开启日志调试 三、生成实体安装sea-orm-cli创建数据库表使用sea-orm-cli命令生成实体文件代码 四、增删改查实现新增数据主键查找条件查找查找用户名…...

SQL中limit的用法

在SQL中&#xff0c;LIMIT是一个用于限制返回结果行数的关键词。它可用于在查询结果中指定返回的行数&#xff0c;从而可以用于分页查询或限制结果集大小。 LIMIT关键词有两种常用的语法格式&#xff1a; LIMIT offset, count&#xff1a;该语法用于指定返回结果的起始位置和…...

vue3 [Vue warn]: Unhandled error during execution of scheduler flush

文章目录 前言一、报错截图二、排除问题思路相关问题 Vue3 优雅解决方法异步组件异同之处&#xff1a;好处&#xff1a;在使用异步组件时&#xff0c;有几个注意点&#xff1a; vue3 定义与使用异步组件 总结 前言 Bug 记录。开发环境运行正常&#xff0c;构建后时不时触发下面…...

【vue2源码】阶段一:Vue 初始化

文章目录 一、项目目录1、主目录2、打包入口 二、构造函数Vue的初始化1、创建 Vue 构造函数2、初始化内容分析2.1 initMixin2.2 stateMixin2.3 eventsMixin2.4 lifecycleMixin2.5 renderMixin 一、项目目录 源码版本&#xff1a;2.7.16 1、主目录 src |-- compiler # 包…...

14.java集合

文章目录 概念Collection 接口概念示例 Iterator 迭代器基本操作&#xff1a;并发修改异常增强循环遍历数组&#xff1a;遍历集合&#xff1a;遍历字符串&#xff1a;限制 list接口ListIteratorArrayList创建 ArrayList&#xff1a;添加元素&#xff1a;获取元素&#xff1a;修…...

二叉树顺序结构堆实现

目录 Test.c测试代码 test1 test2 test3 &#x1f387;Test.c总代码 Heap.h头文件&函数声明 头文件 函数声明 &#x1f387;Heap.h总代码 Heap.c函数实现 ☁HeapInit初始化 ☁HeapDestroy销毁 ☁HeapPush插入数据 【1】插入数据 【2】向上调整Adjustup❗ …...

正则表达式 与文本三剑客(sed grep awk)

一&#xff0c;正则表达式 &#xff08;一&#xff09;正则表达式相关定义 1&#xff0c;正则表达式含义 REGEXP&#xff1a; Regular Expressions&#xff0c;由一类特殊字符及文本字符所编写的模式&#xff0c;其中有些字符&#xff08;元字符&#xff09;不表示字符字面意…...

【XR806开发板试用】全志 XR806 OpenHarmony 鸿蒙系统固件烧录

大家好&#xff0c;我是极智视界&#xff0c;本教程详细记录了全志 XR806 OpenHarmony 鸿蒙系统固件烧录的方法。 在上一篇文章《【嵌入式AI】全志 XR806 OpenHarmony 鸿蒙系统固件编译》中咱们已经编译生成了系统镜像&#xff0c;这里把这个编译出来的镜像烧录到 XR806 板子里…...

linux环境安装git、maven、jenkins等

重启 jenkins的命令&#xff1a; systemctl start jenkins 如果没有vim 命令 可以使用 yum install vim 安装 vim git 下载包地址 https://www.kernel.org/pub/software/scm/git/git-2.28.0.tar.gz 1.安装依赖环境&#xff1a; yum install -y curl-devel expat-devel ge…...

RabbitMQ快速上手

首先他的需求实在什么地方。我美哟明显的感受到。 它给我的最大感受就是脱裤子放屁——多此一举&#xff0c;的感觉。 他将信息发送给服务端中间件。在由MQ服务器发送消息。 服务器会监听消息。 但是它不仅仅局限于削峰填谷和稳定发送信息的功能&#xff0c;它还有其他重要…...