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 倍!
我画个表格对比一下这两种打包方式:
jar | Native Image | |
---|---|---|
包大小 | 18.9MB | 82MB |
编译时间 | 3.7s | 4分54s |
启动时间 | 1.326s | 0.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控制器: 控制器负责处理传入请求并向客户端返回响应。 为了创建基本控制器,我们使用类和装饰器。装饰器将类与所需的元数据相关联,并使 Nest 能够创建路由映射(将请求绑定到相应的控制器)。 1.获取get请求传参…...

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

用的到的linux-文件移动-Day2
前言: 在上一节,我们复习了cd大法和创建生成文件和文件夹的方法,介绍了一些“偷懒”(高效)的小技巧,本节,我们一起来探讨下,我们对文件移动操作时有哪些可以偷懒的小技巧~ 一、复制…...

红队打靶练习: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】文件修改记录
是的,在Linux上,您可以使用’ find 命令检查最近修改的文件。此实用程序可以搜索在指定天数内修改过的文件。你可以这样使用它: 查找主目录中最近24小时(1天)内修改过的文件。 find ~ -type f -mtime -1命令说明: -“~”表示您的主目录。 ’ -type f…...

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

存内计算技术—解决冯·诺依曼瓶颈的AI算力引擎
文章目录 存内计算技术背景CSDN首个存内计算开发者社区硅基光电子技术存内计算提升AI算力知存科技存算一体芯片技术基于存内计算的语音芯片的实现挑战 参考文献 存内计算技术背景 存内计算技术是一种革新性的计算架构,旨在克服传统冯诺依曼架构的瓶颈,并…...
数据结构--树
一、树的基本术语 结点:树中的一个独立单元 结点的度:结点下分支的个数 树的度:树中所有结点中度的最大值 非终端结点:度不为0的结点 双亲和孩子:结点下的子树称为该结点的孩子.相应地,该结点称为孩子的双亲 兄弟:同一个双亲的孩子之间 祖先:从根到该结点所经分支上的所…...

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

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

神经网络的一些常规概念
epoch:是指所有样本数据在神经网络训练一次(单次epoch(全部训练样本/batchsize)/iteration1)或者(1个epochiteration数 batchsize数) batch-size:顾名思义就是批次大小,也就是一次训练选取的样…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...