Java性能优化学习1:理论基础学习与分析

简介: 性能:使用有限的资源在有限的时间内完成工作。最主要的衡量因素就是时间,所以很多衡量指标,都可以把时间作为横轴。

性能是什么


性能:使用有限的资源在有限的时间内完成工作。

最主要的衡量因素就是时间,所以很多衡量指标,都可以把时间作为横轴。


加载缓慢的网站,会受到搜索排名算法的惩罚,从而导致网站排名下降。 因此加载的快慢是性能优化是否合理的一个非常直观的判断因素,但性能指标不仅仅包括单次请求的速度,它还包含更多因素。


性能衡量指标


1、性能指标:吞吐量QPS/TPS/HPS、响应速度

2、响应时间:平均响应时间AVG、百分位数

3、并发量

4、秒开率

5、正确性


吞吐量与响应速度


现在市面上大多数都是分布式的高并发应用,而现在最常用的衡量指标就是吞吐量和响应速度。


如何理解吞吐量和响应速度呢。

可以以路口的红绿灯为例子来模拟,当交通繁忙的时候,如果红绿灯放行时间很长,那么一定会有某一条路口排很长的队,这个时候对于某一辆在排队的车来说,从他等待排队到经过这个红绿灯路口的时间,就是响应时间。

那么吞吐量可以理解为和响应速度有个相对的概念,如果红绿灯时间我们让他变短,那么这个时候每个路口对于某些车辆来说,响应时间可能会变短,但是这个时候频繁的切换红绿灯,就会导致单位时间通过的车辆减少,所以可以认为吞吐量减少了。

这里给定几个确定的开发常用词:


QPS 代表每秒查询的数量,

TPS 代表每秒事务的数量,

HPS 代表每秒的 HTTP 请求数量等,

这都是常用的与吞吐量相关的量化指标。


需要注意的是:在性能优化的时候,我们要搞清楚优化的目标,到底是吞吐量还是响应速度。


有些时候,虽然响应速度比较慢,但整个吞吐量却非常高,比如一些数据库的批量操作、一些缓冲区的合并等。虽然信息的延迟增加了,但如果我们的目标就是吞吐量,那么这显然也可以算是比较大的性能提升。


一般认为:

响应速度是串行执行的优化,通过优化执行步骤解决问题;

吞吐量是并行执行的优化,通过合理利用计算资源达到目标。

一般对于普通的业务来说,优化主要侧重于响应速度,如果响应速度提升了,吞吐量也就提升了。


而现在的环境是追求高并发、高可用、能够抵御住双十一特别大人流的场景、甚至可能还会夹带有云原生的概念,而这些业务场景,两者是都需要的。


响应时间衡量


如何计算响应时间呢?


平均响应时间

也就是常说的指标AVG,能够体现某个业务的平均处理能力。全部加起来除以总数即可。

但是这么算也有弊端,那就是如果有长尾请求的话,长尾请求的影响会被很快平均,导致很多用户的请求变慢,这不能体现在平均耗时指标中。(什么是长尾请求:明显高于均值的那部分占比比较小的请求,业界关于延迟有一个P99的标准,也就是说99%的请求延迟要满足在一定耗时内,剩下的1%会大于这个耗时,而这个1%就会认为是长尾请求。)

既然这里提到了长尾请求,那就顺带讲解一下长尾导致的危害、造成的原因、解决方案。

长尾危害:假设,一个接口提供服务B,有1%的可能性响应时间大于1s,如果此刻一个上游服务A需要完成一次查询,需要同时查询100次的话,那么服务A响应时间超过1s的概率是63%。


0.99的概率是小于1s,100次的概率是0.99^100 = 0.37,小于1s响应的时间是37%的概率。


那么请求大于1s的概率就是63%。


即使服务处理时间超过1秒的比例仅为 0.01% ,当需要同时查询的实例数(Numbers of Servers)达到2000时,服务延时大于1秒的请求数将超过18%。


造成长尾的原因:

共享资源竞争, 周期性的垃圾回收, 运维活动(比如日志备份), 硬件或者软件故障,网络的抖动,都有可能造成。

解决长尾的方法:

微博motan有一种双发机制,它可以有效解决长尾问题,同时能提升系统吞吐量。

传统解决接口超时问题可能通过重试,在一次请求发送之后等待指定的超时时间,如果没有返回则再请求一次,最差情况下要消耗 2 倍的超时时间。


而双发机制则不然,在发送一次请求后等待 P90(在 T1 时间内有 90% 的请求都能返回则称 P90=T1,通常系统的 P90 和程序设置的超时时间相比小很多)时间。


如果请求没有返回则在此刻再次发送一次请求,在超时时间内,这两个请求中取最快返回的那个。


当然,这里有个防雪崩机制,假如,超过一定数量的请求(比如 15%)都在进行双发,则认为服务整体有问题,会自动停止双发。实践证明,双发机制的去长尾效果非常明显


话说回来,为了解决平均响应时间的缺点,同时还要衡量响应时间,我们引入了百分位数概念。


百分位数

这个也比较好理解。圈定一个时间范围,把每次请求的耗时加入一个列表中,然后按照从小到大的顺序将这些时间进行排序。这样,我们取出特定百分位的耗时,这个数字就是 TP 值。可以看到,TP 值(Top Percentile)和中位数、平均数等是类似的,都是一个统计学里的术语。

它的意义是,超过 N% 的请求都在 X 时间内返回。比如 TP90 = 50ms,意思是超过 90th 的请求,都在 50ms 内返回。


这个指标也是非常重要的,它能够反映出应用接口的整体响应情况。比如,某段时间若发生了长时间的 GC,那它的某个时间段之上的指标就会产生严重的抖动,但一些低百分位的数值却很少有变化。


我们一般分为 TP50、TP90、TP95、TP99、TP99.9 等多个段,对高百分位的值要求越高,对系统响应能力的稳定性要求越高。


在这些高稳定性系统中,目标就是要干掉严重影响系统的长尾请求。这部分接口性能数据的收集,会采用更加详细的日志记录方式,而不仅仅靠指标。比如,将某个接口,耗时超过 1s 的入参及执行步骤,详细地输出在日志系统中。


并发量

并发量是指系统同时能处理的请求数量,这个指标反映了系统的负载能力。


在高并发应用中,仅仅高吞吐是不够的,它还必须同时能为多个用户提供服务。并发高时,会导致很严重的共享资源争用问题,我们需要减少资源冲突,以及长时间占用资源的行为。


针对响应时间进行设计,一般来说是万能的。因为响应时间减少,同一时间能够处理的请求必然会增加。值得注意的是,即使是一个秒杀系统,经过层层过滤处理,最终到达某个节点的并发数,大概也就五六十左右。我们在平常的设计中,除非并发量特别低,否则都不需要太过度关注这个指标。


秒开率

现在的用户,如果上网,试想一下,打开一个页面如果要5-6秒那这个网页或者说APP估计评分肯定非常低,秒开是一种特别需要重视的用户体验。


正确性

在进行测试的时候,发现接口响应非常流畅,把并发数增加到 20 以后,应用接口响应依旧非常迅速。


但等应用真正上线时,却发生了重大事故,这是因为接口返回的都是无法使用的数据。


其问题原因也比较好定位,就是项目中使用了熔断。在压测的时候,接口直接超出服务能力,触发熔断了,但是压测并没有对接口响应的正确性做判断,造成了非常低级的错误。


优化的理论方法


理论方法有很多,如木桶理论、基础测试、Amdahal定律等等。


1、木桶理论

一只木桶若想要装最多的水,则需要每块木板都一样长而且没有破损才行。如果有一块木板不满足条件,那么这只桶就无法装最多的水。


能够装多少水,取决于最短的那块木板,而不是最长的那一块。


木桶效应在解释系统性能上,也非常适合。组成系统的组件,在速度上是良莠不齐的。系统的整体性能,就取决于系统中最慢的组件。


比如,在数据库应用中,制约性能最严重的是硬盘的 I/O 问题,也就是说,硬盘是这个场景下的短板,我们首要的任务就是补齐这个短板。


2、基准测试、预热

基准测试(Benchmark)并不是简单的性能测试,是用来测试某个程序的最佳性能。


应用接口往往在刚启动后都有短暂的超时。在测试之前,需要对应用进行预热,消除 JIT 编译器等因素的影响。而在 Java 里就有一个组件,即 JMH,就可以消除这些差异。


优化注意事项


1、把性能分析放在第一位而不是性能优化

在优化的时候不能凭借对代码的熟悉来猜测系统的问题所在,一般来说,复杂的业务系统往往有多个影响因素,我们应该将性能分析放在第一位,而不是把性能优化放在第一位。


进行性能优化时,我们一般会把分析后的结果排一个优先级(根据难度和影响程度),从大处着手,首先击破影响最大的点,然后将其他影响因素逐一击破。


有些优化会引入新的性能问题,有时候这些新问题会引起更严重的性能下降,你需要评估这个连锁反应,确保这种优化确实需要,同时需要使用数字去衡量这个过程,而不是靠感觉猜想。


2、进行大量试验,不依靠小数据量

个体请求的小批量数据,可参考价值并不是非常大。响应时间可能因用户的数据而异,也可能取决于设备和网络条件。


合理的做法,是从统计数据中找到一些规律,比如上面所提到的平均响应时间、TP 值等,甚至是响应时间分布的直方图,这些都能够帮我们评估性能质量。


3、不要过早优化和过度的优化

虽然性能优化有这么多好处,但并不代表我们要把每个地方都做到极致,性能优化也是要有限度的。程序要运行地正确,要比程序运行得更快还要困难。


计算机科学的鼻祖"Donald Knuth" 曾说:“过早的优化是万恶之源”,就是这个道理。


正确的做法是,项目开发和性能优化,应该作为两个独立的步骤进行,要做性能优化,要等到整个项目的架构和功能大体进入稳定状态时再进行。


4、保持良好的编程习惯


相关文章
|
22天前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
29 0
|
1月前
|
存储 算法 Java
Java内存管理深度剖析与优化策略####
本文深入探讨了Java虚拟机(JVM)的内存管理机制,重点分析了堆内存的分配策略、垃圾回收算法以及如何通过调优提升应用性能。通过案例驱动的方式,揭示了常见内存泄漏的根源与解决策略,旨在为开发者提供实用的内存管理技巧,确保应用程序既高效又稳定地运行。 ####
|
23天前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
14天前
|
存储 Java
Java 11 的String是如何优化存储的?
本文介绍了Java中字符串存储优化的原理和实现。通过判断字符串是否全为拉丁字符,使用`byte`代替`char`存储,以节省空间。具体实现涉及`compress`和`toBytes`方法,前者用于尝试压缩字符串,后者则按常规方式存储。代码示例展示了如何根据配置决定使用哪种存储方式。
|
19天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
23天前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
49 5
|
24天前
|
监控 算法 Java
jvm-48-java 变更导致压测应用性能下降,如何分析定位原因?
【11月更文挑战第17天】当JVM相关变更导致压测应用性能下降时,可通过检查变更内容(如JVM参数、Java版本、代码变更)、收集性能监控数据(使用JVM监控工具、应用性能监控工具、系统资源监控)、分析垃圾回收情况(GC日志分析、内存泄漏检查)、分析线程和锁(线程状态分析、锁竞争分析)及分析代码执行路径(使用代码性能分析工具、代码审查)等步骤来定位和解决问题。
|
21天前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
24天前
|
存储 缓存 安全
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
35 4
|
28天前
|
监控 算法 Java
Java虚拟机垃圾回收机制深度剖析与优化策略####
【10月更文挑战第21天】 本文旨在深入探讨Java虚拟机(JVM)中的垃圾回收机制,揭示其工作原理、常见算法及参数调优技巧。通过案例分析,展示如何根据应用特性调整GC策略,以提升Java应用的性能和稳定性,为开发者提供实战中的优化指南。 ####
41 5