引言

现状和背景

Spring框架是广泛使用的Java开发框架之一,它提供了强大的功能和灵活性,但在大型应用中,由于Spring框架的复杂性和依赖关系,应用的启动时间和性能可能会受到影响。这可能导致开发过程中的迟缓和开发效率低下。优化Spring应用程序的启动速度和性能是一个重要的任务,通过分析和优化应用的初始化过程、减少不必要的依赖和组件加载、并利用异步初始化、懒加载等技术,可以显著改善应用的启动性能。这将帮助开发者提高开发效率、减少调试时间,并提供更好的用户体验。

线上的业务 jar 包基本上普遍比较庞大,动不动一个 jar 包几百 M,启动时间在10分钟级,拖慢了我们在故障时快速扩容的响应、以及本地开发调试效率。于是做了一些分析,看看 Spring 程序启动慢到底慢在哪里,如何去优化,目前的效果是大部分大型应用启动时间可以缩短 70%~80%。

主要有下面这些内容

  • SpringBean 加载耗时 timeline 可视化分析()
  • SpringBean 的可视化依赖分析()
  • 应用未加载的jar包(Jar瘦身)()
  • 应用启动过程线程wall clock火焰图()

重要性和影响

开发效率提高:较快的应用启动速度可以显著减少开发和调试的时间。开发人员能够更快地启动应用程序,进行功能测试和调试,从而提高开发效率和迭代速度。

部署和扩展效率提升:优化启动速度可以减少部署和扩展应用程序的时间和成本。快速启动的应用程序可以更快地响应负载变化,提高系统的可伸缩性和弹性。

资源利用率优化:通过减少初始化时间和优化资源加载,可以降低应用程序的内存和CPU占用率。这有助于提高服务器的利用率,并降低运行应用程序的成本。

分析工具

  • Arthas:Arthas是一个开源的Java诊断工具,可以实时监控和诊断Java应用程序。它提供了丰富的命令和功能,用于分析应用程序的性能问题,包括启动过程中的资源消耗和加载时间。
  • JVM Sandbox:JVM Sandbox是一种基于Java安全管理器的技术,用于隔离和限制Java应用程序的访问权限。它可以帮助减少启动时的资源消耗和加载时间,提高应用程序的启动速度。
  • Async Profiler:Async Profiler是一个低开销的异步Java性能分析工具,用于收集和分析应用程序的性能数据。它可以帮助你找出启动过程中的性能瓶颈,以及其他影响启动速度的问题。
  • 启动加速-异步初始化方法:异步初始化方法是一种启动加速的技术,通过将一些初始化任务异步执行,可以减少启动时间并提高应用程序的响应性。这可以通过使用线程池、异步框架或异步注解等方式来实现。
  • Spring Boot Startup ReportSpring Boot Startup Report是一个用于生成Spring Boot应用程序启动报告的工具。它可以提供详细的启动过程信息,包括每个bean的加载时间、自动配置的耗时等,帮助你分析和优化启动过程。
  • Jaeger UI Jaeger UI是一个用于可视化和分析分布式追踪数据的工具。通过使用Jaeger UI,你可以监控和分析应用程序的启动过程,识别潜在的性能问题和瓶颈。
  • Spring Startup Analyzer:Spring Startup Analyzer是一个用于采集Spring应用程序启动过程数据并生成交互式分析报告的工具。它的目标是帮助分析Spring应用程序的启动卡点,并支持Spring Bean的异步初始化,以减少优化Spring应用程序的启动时间。该工具支持在Linux、Mac和Windows操作系统上运行,并参考了spring-boot-startup-report实现其用户界面。使用Spring Startup Analyzer,可以收集应用程序的启动过程数据,并生成可视化的HTML报告。这个报告可以帮助你分析Spring应用程序的启动性能,并找出潜在的优化机会。

Spring Startup Analyzer优化方案

借助Spring startup analyzer的能力,我们以业务线的ARK项目为例,深入研究如何优化提效Spring项目的启动过程。下面我们先观察下ARK的基本启动情况:

启动概览

  • Startup Time(s):启动时长
  • Num of Bean:初始化的Bean数量
  • Used/Total Jars:使用Jar数量/总量
  • Unused/Total Jars:未使用Jar数量/总量
  • ClassLoader Count:类加载器数量

Spring Bean初始化详情

  • Name:一级name对应着Bean的名称
  • Duration with children (ms) :Bean的引用加载时长
  • Duration (ms) :Bean本身的加载时长
  • Detail:包含类加载器、加载该Bean的线程信息(异步加载的话会有多个不同的)

SpringBean 加载耗时 timeline 可视化分析

这个观察项可以一直下探,直到Bean引用的最末级,可以看出每一级的加载时长

应用启动过程线程wall clock火焰图

如何看懂火焰图

y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。

x 轴表示抽样数,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。

火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题。

颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调

火焰图总览

从总览图中可以看出,有三个入口函数占用百分比较大,下面分别看一下

火焰局部图1

这部分火焰图可以看出,springfox在启动过程做了很多初始化,占了大量时间,对于不需要该功能的项目,可以直接下掉

火焰局部图2

了解下spring bean 的初始化过程

从这个图中可以看出,bean的创建过程也占了很多时间

火焰局部图3

从这个图中可以看出,注册BeanPostProcessor也耗费了大量时间

应用未加载的jar包(Jar瘦身)

这一个观察项可以搜集到项目启动完之后,没有用到的Jar包

实施与优化效果

操作步骤和配置项

安装Spring Startup Analyzer

手动安装

  1. 点击realease下载最新版tar.gz包

  2. 新建文件夹,并解压

linux/mac系统可以考虑使用以下命令:

mkdir -p ${HOME}/spring-startup-analyzercd 下载路径
tar -zxvf spring-startup-analyzer.tar.gz -C 安装路径/spring-startup-analyzer

脚本安装(linux/mac)

curl -sS https://raw.githubusercontent.com/linyimin0812/spring-startup-analyzer/main/bin/install.sh | sh

脚本默认安装路径:$HOME/spring-startup-analyzer

应用启动

spring-startup-analyzer是以agent的方式启动的,所以在启动命令中添加参数-javaagent:安装路径/spring-startup-analyzer/lib/spring-profiler-agent.jar即可。

  • 以java命令行的方式启动应用,则在命令行中添加参数,例如:
java -javaagent:/Users/runner/spring-startup-analyzer/lib/spring-profiler-agent.jar \
-Dproject.name=mac-demo \
-Dspring-startup-analyzer.admin.http.server.port=8066 \
-jar /Users/runner/spring-startup-analyzer/ARK.jar
  • IDEA中启动,则需要在VM options选项中添加:

日志文件路径:安装路径/spring-startup-analyzer/logs

  • startup.log: 启动过程中的日志
  • transform.log: 被re-transform的类/方法信息

应用启动完成后会在console和startup.log文件中输出======= spring-startup-analyzer finished, click http://localhost:xxxx to visit details. ======,可以通过此输出来判断采集是否完成。

启动时间和性能改善情况

优化之前

预发平均启动10分钟,本地无法启动,每次需求需要提交到预发环境验证,开发和发版周期比较长,且预发环境连接的生产库,不能随便造数。项目引用585个jar,其中有337个jar没用到。

慢bean分析

分析可以看到,耗时排名前面的接口都是jsf相关的加载,还有一个es相关的bean。

功能路径:Details of Method Invoke --> AbstractAutowireCapableBeanFactory.createBean

jsf启动优化

注:index=“注册中心地址”中的“注册中心地址“做了匿名,在具体场景查看自己代码中的配置

jsf的生产者的注册中心在启动的时候,会拉取一批ip,不断尝试注册jsf,在办公环境这些ip无法访问,导致启动过程一直重试

    <!-- 预发、生产的注册中心 -->
<jsf:registry id="jsfRegistry" protocol="jsfRegistry" index="注册中心地址"/>

在本机host里面增加jsf发布地址的host配置,下面*...* 在使用的时候替换成自己的,可以 ping test.注册中心地址 获取。“注册中心地址” 替换成上面index后面的地址

*.*.*.* 注册中心地址 

再次启动项目,时长来到185s

开启Bean懒加载

将ES的Bean初始化进行懒加载,以及开启全局懒加载,时长来到131s;

全局懒加载:

1、根据spring版本的不同,开启全局懒加载的方式可能会不相同

2、不建议生产环境开启全局懒加载,因为基本上我们的服务都是部署在k8s上的,有可能服务在伸缩的时候,在访问量大的时候,由于懒加载的配置,服务快速启动成功了,会返回给docker容器服务已经准备就绪状态,导致k8s把流量分给该服务,导致预想不到的问题。

Jar瘦身

对于应用未使用的jar包,可以谨慎剔除,在剔除的时候一个一个下,每下一个都要重复编译和启动验证是否会对项目造成影响,这是一个持续和长期的过程,Jar瘦身不仅对启动时长有收益,而且对编译提效很明显,减少了大量的Jar复制过程

最终效果

做完上述优化之后:

  • 本地能够启动和debug项目,这对开发人员来说有极大的提效。
  • 预发使用该方案进行优化之后,能够缩短项目编译以及发布的时间,对于快速验证和迭代需求有极大提效。
  • 整体启动效率提升70%~80%。
  • 在intel芯片电脑,启动速度在2min11s。
  • 在m1芯片的电脑,速度会更快,大概启动时间在90s左右。
  • 使用该思路,可以优化大部分spring以及spring boot项目,建议定期做一轮这种排查和优化。

优化关键点和方法

  • 去除未使用的jar包:定位未使用的jar包。通过分析和整理项目依赖,可以将这些未使用的jar包从应用中移除,减少编译、启动时间和资源消耗。
  • 优化慢速的Bean初始化:找到启动耗时较长的Bean。可以考虑对这些接口和Bean进行优化,例如使用延迟加载或异步加载的方式,以减少启动时的耗时。
  • 取消不需要的发布:对于本地开发环境而言,如果不需要发布jsf接口,可以在本地取消这部分的发布,以节省启动时间。
  • 开启全局懒加载:通过开启全局懒加载,可以延迟加载一些不必要的组件和资源,从而减少启动时间。确保在需要使用时才进行加载。
  • 拆分大型组件:定位加载时间较长的组件,可以考虑将其拆分成多个组件,并在启动时只加载需要的部分。这样可以减少启动时的加载时间和资源消耗。
  • 使用性能分析工具:结合之前提到的性能分析工具,如Spring Startup Analyzer、Java Profiler、VisualVM等,对应用进行性能分析。通过监测和分析应用的性能数据,可以找到性能瓶颈,并针对性地进行优化。
  • 定期进行代码优化和重构:定期审查和优化代码,识别和消除潜在的性能问题。使用优化的算法和数据结构,减少不必要的计算和循环,优化数据库查询等,以提高应用的性能。
  • 使用缓存机制:合理地使用缓存来减少对数据库或其他资源的频繁访问。通过缓存常用数据或计算结果,可以显著提升应用的响应速度和性能。
  • 并行化处理:如果有一些独立的任务可以并行处理,可以考虑使用多线程或异步机制来提高处理速度和效率。

信息补充

oracle jdk8下载地址

https://www.oracle.com/java/technologies/downloads/#java8-mac

oracle登录账号

请联系作者提供免费账号

本地redis安装

https://redis.io/docs/install/install-redis/install-redis-on-windows/

spring-startup-analyzer启动分析工具

https://github.com/linyimin0812/spring-startup-analyzer/blob/main/README_ZH.md

作者:京东健康 梁灿

来源:京东云开发者社区 转载请注明来源

高效开发与设计:提效Spring应用的运行效率和生产力的更多相关文章

  1. 利用低代码优化人力资源配置,为软件开发降本提效 ZT

    低代码 是一种主要应用于企业信息化领域的快速开发技术.借助低代码,开发者无需编码即可生成企业应用的常见功能,少量编码能开发出更多扩展功能.有了低代码技术,IT团队甚至业务团队都可以参与到编写应用程序当 ...

  2. Struts2、Spring、Hibernate 高效开发的最佳实践(转载)

    Struts2.Spring.Hibernate 高效开发的最佳实践 Struts2.Spring.Hibernate(SSH)是最常用的 Java EE Web 组件层的开发技术搭配,网络中和许多 ...

  3. Spring Boot 2.X(十九):集成 mybatis-plus 高效开发

    前言 之前介绍了 SpringBoot 整合 Mybatis 实现数据库的增删改查操作,分别给出了 xml 和注解两种实现 mapper 接口的方式:虽然注解方式干掉了 xml 文件,但是使用起来并不 ...

  4. 阿里巴巴如何进行测试提效 | 阿里巴巴DevOps实践指南

    编者按:本文源自阿里云云效团队出品的<阿里巴巴DevOps实践指南>,扫描上方二维码或前往:https://developer.aliyun.com/topic/devops,下载完整版电 ...

  5. 声网Agora发布教育信息化解决方案 助力教育公平提效

    4月23日-25日,由中国教育装备行业协会主办的第79届教育装备展在厦门国际会展中心举办.作为赋能教育信息化的实时互动PaaS服务商,声网Agora应邀参会.展会现场,声网展示了基于实时音视频互动能力 ...

  6. .Net 高效开发之不可错过的实用工具(转)

    .Net 高效开发之不可错过的实用工具(转) 本文摘自: http://www.cnblogs.com/powertoolsteam/p/5240908.html#3372237 Visual Stu ...

  7. WPF Multi-Touch 开发:高效开发模式

    原文 WPF Multi-Touch 开发:高效开发模式 在前几篇文章中已经介绍了触屏操作的多种模式,并对其开发方式也有了进一步了解.细心的朋友应该会发现在上一篇文章中,如果拖动图片过快它会因惯性效果 ...

  8. Net 高效开发

    Net 高效开发之不可错过的实用工具   工欲善其事,必先利其器,没有好的工具,怎么能高效的开发出高质量的代码呢?本文为各ASP.NET 开发者介绍一些高效实用的工具,涉及SQL 管理,VS插件,内存 ...

  9. .NET 高效开发之不可错过的实用工具(第一的当然是ReSharper插件)

    工欲善其事,必先利其器,没有好的工具,怎么能高效的开发出高质量的代码呢?本文为 ASP.NET 开发者介绍一些高效实用的工具,包括 SQL 管理,VS插件,内存管理,诊断工具等,涉及开发过程的各个环节 ...

  10. NET 高效开发之不可错过的实用工具(第一的当然是ReSharper插件)

    工欲善其事,必先利其器,没有好的工具,怎么能高效的开发出高质量的代码呢?本文为 ASP.NET 开发者介绍一些高效实用的工具,包括 SQL 管理,VS插件,内存管理,诊断工具等,涉及开发过程的各个环节 ...

随机推荐

  1. react中使用动画 react-transition-group

    在React中通过react-transition-group使用过渡.动画,首先要有CSS3中的过渡和动画的相关知识储备,可以参考 过渡和2D变换.动画和3d变换. 我们自己通过css设置过渡.动画 ...

  2. 利用pytorch自定义CNN网络(一):torchvision工具箱

    本文是利用pytorch自定义CNN网络系列的第一篇,主要介绍 torchvision工具箱及其使用,关于本系列的全文见这里. 笔者的运行设备与软件:CPU (AMD Ryzen 5 4600U) + ...

  3. cs50ai0----search

    cs50ai0-------Search cs50ai0-------Search 基础知识 课后题目 代码实践 学习链接 总结 基础知识 (1) search problem 上图是搜索问题的一般形 ...

  4. 干货分享:用ChatGPT调教批量出Midjourney咒语,出图效率Nice ,附资料。

    Prompts就是AI绘图的核心竞争力. 您是不是觉得用Midjourney生成的图不够完美? 又让ChatGPT去生成Prompt,然后效果还不理想? 其实ChatGPT你给他投喂资料后,经过调教的 ...

  5. 《数据结构-C语言》顺序表

    @ 目录 顺序表 结构定义 初始化 创建表 求表长 判断表是否为空 取值 查找 插入 删除 逆置 清空 销毁 遍历打印 测试 顺序表 结构定义 #include <stdio.h> #in ...

  6. Linux下MySQL备份指定数据库命令

    比如我们要备份mysql中已经存在的名为linux的数据库,要用到命令mysqldump 命令格式如下: [root@linuxsir01 root]# mysqldump -u root -p li ...

  7. PostgreSQL-分区表介绍

    一.分区简介 表分区是解决一些因单表过大引用的性能问题的方式,比如某张表过大就会造成查询变慢,可能分区是一种解决方案.一般建议当单表大小超过内存就可以考虑表分区了. 表的分区就是将一个逻辑上的大表(主 ...

  8. 解决 wg-quick 在 Mac 上 bash 3 无法运行的问题

    问题原因 我可以理解,开发人员不想使用苹果使用的旧bash v3.但从用户的帖子来看,安装一个较新的bash并不那么好 所以我看了wireguard的wg-quick.需要支持的唯一变化,两个bash ...

  9. Linux服务器搭建环境复盘

    Linux服务器搭建环境复盘 Linux服务器上是没有开发环境的,需要自己配置,在获得了服务器账号后,通过WinSCP登录可以传文件. 下载anaconda 官网下载Anaconda Linux版本 ...

  10. RocketMQ 消息重试与死信队列

    RocketMQ 消息重试与死信队列 RocketMQ 前面系列文章如下: RocketMQ系列(一) 基本介绍 RocketMQ 系列(二) 环境搭建 RocketMQ 系列(三) 集成 Sprin ...