Spring Boot 启动加速
一、简介
本文将带你了解如何通过调整 Spring 应用的配置、JVM 参数和使用 GraalVM 原生镜像来缩短 Spring Boot 的启动时间。
二、调整 Spring 应用
首先,创建一个 Spring Boot(2.5.4)应用,添加 Spring Web、Spring Actuator 和 Spring Security 依赖。
还要添加 spring-boot-maven-plugin
插件,并配置将应用打包到 jar 文件中:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <configuration> <finalName>springStartupApp</finalName> <mainClass>com.baeldung.springStart.SpringStartApplication</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions>
</plugin>
使用标准的 java -jar
命令运行 jar 文件,并查看应用的启动时间。
c.b.springStart.SpringStartApplication : Started SpringStartApplication in 3.403 seconds (JVM running for 3.961)
如上,应用启动时间约为 3.4 秒。我们把这个时间作为下文调整的参考。
1、延迟初始化
Spring 支持延迟初始化。延迟初始化意味着 Spring 不会在启动时创建所有 Bean。此外,Spring 在需要 Bean 之前不会注入任何依赖。从 Spring Boot 2.2 版开始,就可以使用 application.properties
启用延迟始化:
spring.main.lazy-initialization=true
新建一个 jar 文件并按上例配置、启动后,新的启动时间略有改善:
c.b.springStart.SpringStartApplication : Started SpringStartApplication in 2.95 seconds (JVM running for 3.497)
根据代码规模的大小,延迟初始化可以显著缩短启动时间。启动时间的减少取决于应用的依赖关系。
此外,在开发过程中使用 DevTools 热重启功能时,延迟初始化也有好处。通过延迟初始化增加重启次数,JVM 可以更好地优化代码。
不过,延迟初始化也有一些缺点。最明显的缺点是应用处理第一个请求的速度会变慢,因为 Spring 需要时间来初始化所需的 Bean。另一个缺点是可能会在启动时错过一些错误。这可能会在运行时导致 ClassNotFoundException
。
2、排除不必要的自动配置
Spring Boot 的理念是约定大于配置。Spring 可能会初始化应用并不需要的 Bean。我们可以通过启动日志检查所有自动配置的 Bean。
在 application.properties
中将 org.springframework.boot.autoconfigure
的日志级别设置为 DEBUG
:
logging.level.org.springframework.boot.autoconfigure=DEBUG
在日志中,可以看到专门用于自动配置的日志信息,从以下几行开始:
============================
CONDITIONS EVALUATION REPORT
============================
通过这个日志报告,可以使用 @EnableAutoConfiguration
排除应用中不会用到的自动配置:
@EnableAutoConfiguration(exclude = {JacksonAutoConfiguration.class, JvmMetricsAutoConfiguration.class, LogbackMetricsAutoConfiguration.class, MetricsAutoConfiguration.class})
如上,不使用 Jackson JSON 和一些指标配置,就可以节省一些启动时间:
c.b.springStart.SpringStartApplication : Started SpringStartApplication in 3.183 seconds (JVM running for 3.732)
3、其他小调整
Spring Boot 自带一个嵌入式 servlet 容器。默认情况下,使用的是 Tomcat。虽然 Tomcat 在大多数情况下已经足够好,但其他 servlet 容器的性能可能更高。在测试中,JBoss 的 Undertow 比 Tomcat 或 Jetty 性能更好。它需要的内存更少,平均响应时间也更长。
修改 pom.xml
,切换到 Undertow:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
在 classpath 扫描方面可以有以下小改进。Spring 的 classpath 扫描速度很快。当代码库较大时,可以通过创建静态索引来缩短启动时间。
添加一个依赖 spring-context-indexer
来生成索引。Spring 不需要任何额外配置。在编译时,Spring 会在 META-INF\spring.components
中创建一个额外的文件。Spring 会在启动时自动使用该文件:
<dependency><groupId>org.springframework</groupId><artifactId>spring-context-indexer</artifactId><version>${spring.version}</version><optional>true</optional>
</dependency>
由于我们只有一个 Spring 组件,因此这一调整在测试中没有产生显著效果。
application.properties
(或 .yml
)配置文件有几个有效的存放位置,最常见的是在 classpath 根目录下或与 jar 文件在同一文件夹下。我们可以通过使用 spring.config.location
参数设置显式路径来避免搜索多个位置,从而节省几毫秒的搜索时间:
java -jar .\target\springStartupApp.jar --spring.config.location=classpath:/application.properties
最后,Spring Boot 提供了一些 MBean,用于通过 JMX 监控应用。完全关闭 JMX 可以避免创建这些 Bean 的成本:
spring.jmx.enabled=false
三、调整 JVM
1、Verify 参数
此参数用于设置字节码验证模式。字节码验证可确定类的格式是否正确,是否符合 JVM 规范约束。
该参数有几个选项:
-Xverify
是默认值,可对所有非 “boot” 类进行验证。-Xverify:all
可对所有类进行验证。这种设置会对启动性能产生很大的负面影响。-Xverify:none
(或-Xnoverify
)该选项可完全禁用校验器,大大缩短启动时间。
在启动 JVM 时设置此参数。
从 JDK 13 开始及其后续版本中,不建议继续使用 -Xverify:none
和-noverify
参数。
java -jar -noverify .\target\springStartupApp.jar
JVM 会警告这个选项已被弃用,此外,启动时间也会缩短:
c.b.springStart.SpringStartApplication : Started SpringStartApplication in 3.193 seconds (JVM running for 3.686)
这个选项会有一个重大的问题,可能导致应用在运行时因错误而崩溃,而这些错误本应该提前捕获到的。这也是该选项在 Java 13 中被标记为废弃的原因之一。
2、TieredCompilation
参数
Java 7 引入了分层编译。HotSpot 编译器将对代码进行不同层次的编译。
Java 代码首先被解释为字节码。然后,字节码被编译成机器码。这种编译发生在方法级别。C1 编译器会在调用一定次数后对方法进行编译。在运行更多次后,C2 编译器会对其进行编译,从而进一步提高性能。
使用 -XX:-TieredCompilation
参数,可以禁用中间编译层。这意味着我们的方法将使用 C2 编译器进行解释或编译,以实现最大优化。这不会降低启动速度。
要禁用 C2 编译。可以使用 -XX:TieredStopAtLevel=1
选项。结合 -noverify
参数,可以缩短启动时间。遗憾的是,这会降低 JIT 编译器后期的运行速度。
使用 -XX:TieredStopAtLevel=1
选项就带来了显著的改进:
c.b.springStart.SpringStartApplication : Started SpringStartApplication in 2.754 seconds (JVM running for 3.172)
如果同时使用本节中的 2 个参数,还能进一步缩短启动时间:
java -jar -XX:TieredStopAtLevel=1 -noverify .\target\springStartupApp.jar
c.b.springStart.SpringStartApplication : Started SpringStartApplication in 2.537 seconds (JVM running for 2.912)
四、Spring Native
原生镜像(Native image)是使用 AOT(Ahead-Of-Time)编译器编译的 Java 代码,并打包成可执行文件。它不需要 Java 就能运行。由于没有 JVM 的开销,因此程序运行速度更快,对内存的依赖性也更小。GraalVM 项目引入了原生镜像和所需的构建工具。
Spring Native是一个实验性模块,它支持使用 GraalVM 原生镜像编译器对 Spring 应用程序进行原生编译。AOT 编译器会在构建过程中执行多项任务,从而缩短启动时间(静态分析、删除未使用的代码、创建固定的类路径等)。
原生镜像仍有一些限制:
- 它不支持所有 Java 功能
- 反射功能需要特殊配置
- 延迟类加载不能使用
- Windows 兼容性问题
要将应用编译为原生镜像,需要在 pom.xml
中添加 spring-aot
和 spring-aot-maven-plugin
依赖。Maven 将在 target
文件夹中,通过 package
命令创建原生镜像。
相关文章:
Spring Boot 启动加速
一、简介 本文将带你了解如何通过调整 Spring 应用的配置、JVM 参数和使用 GraalVM 原生镜像来缩短 Spring Boot 的启动时间。 二、调整 Spring 应用 首先,创建一个 Spring Boot(2.5.4)应用,添加 Spring Web、Spring Actuator …...

UDP数据报文格式
...
软考-系统架构-2023-反思
2023年11月4日,参加了软考的高级架构设计考试。针对于这次考试做一些总结和反思。 我的考试准备周期非常长,但是实际的时间非常少。差不多一年前我就开始有这个计划和想法准备考试了,但是前期基本上就是翻翻书,跟没有开始区别并不…...

day52
今日内容概要 web应用程序 手写web框架(帮助我们理解别人写好的成熟框架、重点在于思路的理解、代码无需掌握) Django框架的学习 Python中得主流框架 框架的下载、安装、版本、怎么启动、怎么使用等 三板斧问题 web应用程序 Django框架是一款专门用来开发web应用的框架 …...

Mysql关联查询
Mysql关联查询 1、数据准备 # 班级表 create table class(id int primary key auto_increment,name varchar(20),description varchar(100) );# 学生表 create table student(id int primary key auto_increment,sn varchar(20),name varchar(20),email varchar(20),class_id…...

MOSFET和IGBT栅极驱动器TLP250H(D4-TP1,F)电路的基本原理
TLP250H,TLP250H(D4-TP1,F)是SOP8封装中的光电耦合器,由GaA组成ℓ作为红外发光二极管(LED)光学耦合到集成的高增益、高速光电探测器IC芯片。它在高达125℃的温度下提供有保证的性能和规格. TLP250H具有内部法拉第屏蔽,…...

Vue - Syntax Error: TypeError: this.getOptions is not a function 项目运行时报错,详细解决方案
报错问题 关于此问题网上的教程都无法解决,如果您的报错与本文相似,本文即可 100% 完美解决。 在 vue2.js 项目中,执行 npm run serve 运行时出现如下报错信息, Syntax Error: TypeError: this.getOptions is not a function 解决方案 按照以下步骤,即可完美解决。 这个错…...
C 语言类型转换
C 语言类型转换 类型转换允许我们将一种数据类型转换为另一种数据类型。在C语言中,我们使用强制转换运算符进行类型转换,用(type)表示。 语法: (type)value;注意:始终建议将较低的值转换为较高的值&…...

数据结构-链表的简单操作实现
目录 0.链表前序工作 1.构建出一个链表 2.展示链表中的所有存储数据 3.查找关键字key是否在链表中 4.求链表的长度 5.头插法 6.尾插法 7.插入任意位置(规定第一个元素位置为0下标) 8.删除第一次出现的值为key的关键字 9.删除所有值为key的关键字…...

竞赛选题 深度学习手势识别 - yolo python opencv cnn 机器视觉
文章目录 0 前言1 课题背景2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存 5 模型训练5.1 修…...

【算法练习Day42】买卖股票的最佳时机 III买卖股票的最佳时机 IV
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:练题 🎯长路漫漫浩浩,万事皆有期待 文章目录 买卖股票的最佳时机 III买卖…...

苹果手机如何备份通讯录?看完这篇就懂了!
如果遇到手机丢失或者出现故障的情况,通讯录备份可以避免联系人信息丢失。另外,当用户更换手机或者进行数据迁移时,提前备份好的通讯录数据可以快速还原到新设备上,避免了手动输入联系人的麻烦。苹果手机如何备份通讯录࿱…...

[yarn]yarn异常
一、运行一下算圆周率的测试代码,看下报错 cd /home/data_warehouse/module/hadoop-3.1.3/share/hadoop/mapreduce hadoop jar hadoop-mapreduce-examples-3.1.3.jar pi 1000 1000 后面2个数字参数的含义: 第1个1000指的是要运行1000次map任务 …...
C++ NULL 与nullptr 区别
在编写C程序的时候只看到过NULL,而在C的编程中,我们可以看到NULL和nullptr两种关键字,其实nullptr是C11版本中新加入的,它的出现是为了解决NULL表示空指针在C中具有二义性的问题。 一、C程序中的NULL 在C语言中,NULL…...

Google Chrome 浏览器 119.0.6045.106 版本提示 STATUS_INVALID_IMAGE_HASH 崩溃
问题 今天更新 Google Chrome 浏览器到 119.0.6045.106 版本,然后访问页面不是空白,就是页面崩溃了 解决方案 我在网上找了几种,下面这个方式符合,能解决我的问题,就是在快捷方式的属性那里,找到目标给它…...
网络IO
网络IO 阻塞模型 在之前网络通信都是阻塞模型 客户端向服务端发出请求后,客户端会一直处于等待状态,直到服务器端返回结果或网络出现问题 服务器端也是如此,在处理某个客户端A发来的请求时,另一个客户端B发来的请求会等待…...
数据库管理-第115期 too many open files(202301107)
数据库管理-第115期 too many open files(202301107) 这是我上周末帮朋友站台过程中处理的一个问题。 1 背景 其实这是别人搭的一个使用CentOS 7.8系统安装的一套11.2.0.4(无补丁)的双节点RAC,用于迁移以前运行在So…...
一行命令让你的服务器命令行亮起来!!!!
if [ "$(whoami)" "root" ]; then echo "PS1${debian_chroot:($debian_chroot)}\[\033[01;32m\]\u\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]# " >> /root/.bashrc; source /root/.bashrc; echo "已为root用户设置提示符以#结尾…...

线性代数(二)| 行列式性质 求值 特殊行列式 加边法 归纳法等多种方法
文章目录 1. 性质1.1 重要性质梳理1.1.1 转置和初等变换1.1.2加法行列式可拆分1.1.3 乘积行列式可拆分 1.2 行列式性质的应用1.2.1 简化运算1.2.2 将行列式转换为(二)中的特殊行列式 2 特殊行列式2.1 上三角或下三角行列式2.2 三叉行列式2.3 行列式行和&…...
OpenCV入门7:图像形态学变换
形态学是一种针对图像形状和结构进行操作和分析的图像处理方法。在OpenCV中,提供了一些函数和方法用于执行形态学操作。下面将介绍一些常见的形态学操作及其在OpenCV中的实现方式。 膨胀(Dilation): 膨胀操作可以扩展图像中的边…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

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

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...

Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南
在RK3588上搭建ROS1环境:创建节点与数据可视化实战指南 背景介绍完整操作步骤1. 创建Docker容器环境2. 验证GUI显示功能3. 安装ROS Noetic4. 配置环境变量5. 创建ROS节点(小球运动模拟)6. 配置RVIZ默认视图7. 创建启动脚本8. 运行可视化系统效果展示与交互技术解析ROS节点通…...

归并排序:分治思想的高效排序
目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法,由约翰冯诺伊曼在1945年提出。其核心思想包括: 分割(Divide):将待排序数组递归地分成两个子…...

【技巧】dify前端源代码修改第一弹-增加tab页
回到目录 【技巧】dify前端源代码修改第一弹-增加tab页 尝试修改dify的前端源代码,在知识库增加一个tab页"HELLO WORLD",完成后的效果如下 [gif01] 1. 前端代码进入调试模式 参考 【部署】win10的wsl环境下启动dify的web前端服务 启动调试…...