👋Java性能概述
Java之父 James Gosling 的一句很经典的话:
Java 是一门蓝领语言。它不是博士论文材料,而是用于工作的语言。—— James Gosling
言外之意,Java一直是一种强调实用性的语言。它一开始对性能的态度是,只要环境足够快并且能提升开发效率,就可以牺牲原始性能。所以直到近些年,随着诸如HosSpot之类的JVM日趋成熟和进步,Java环境才开始适合于高i性能计算应用程序。
这种实用性在Java平台中以多种方式体现出来,但最明显的一点就是使用了托管子系统。其理念是,开发人员无需担心托管环境下的某些功能细节,而代价是放弃对底层的一些控制。
最明显的例子当然是内存管理了。JVM以可插拔垃圾收集子系统的形式提供自动内存管理,所以程序员不必手动跟踪内存。
⚽️1. Java做为实验科学的性能
和大多数现代软件系统一样, Java/JVM 软件栈非常复杂。事实上,因为 JVM 具有高度优化和自适应的特性,所以构建于 JVM 之上的生产系统有时会表现出一些非常微妙且复杂的性能行为。这种复杂性和摩尔定律以及该定律所表示的硬件能力的空前增长有关。
计算机软件产业最为惊人的成就是其持续不断地抵消了硬件产业所取得的稳定而
巨大的成果。——Henry Petroski
尽管有些软件系统浪费了硬件产业过去的成果,但是 JVM 所代表的是某种工程上的胜利。从 20 世纪 90 年代末诞生以来, JVM 已经发展为一个高性能的、通用的执行环境,能够很好地利用新的硬件特性。但是,与任何复杂的高性能系统一样, JVM 需要我们有相当程度的技能和经验才能从中获得最大的收益。
如果不能清晰地定义测量,那比无效还要糟糕。——Eli Goldratt
因此, JVM 性能调优是技术、方法论、可测的量和工具的综合。它的目的是以系统所有者或用户所期望的方式产生可以测量的输出。换句话说,性能是一门实验科学,它通过以下方式实现预期结果:
- 定义期望的结果
- 测量现有系统
- 确定要实现需求所需的工作
- 开始某个改进操作
- 重新测试
- 确定目标是否实现
定义和确定预期性能结果的过程创建了一组量化的目标。确定应该测量什么并记录这些目标非常重要,而它们将成为项目工件和可交付成果的一部分。由此我们可以看到,性能分析是建立在定义和实现非功能性需求的基础之上的。
正如前面提到的,这个过程可不是占卜。相反,我们依赖于统计数据和对结果的适当处理。对于很多实际的项目而言,无疑需要对数据和统计有更深入的理解。
⚽️2. Java性能分类方法
接下来介绍一些基本的性能指标,它们为性能分析提供了一个词汇表,让你可以量化调优项目的目标。这些目标就是定义性能目标的非功能性需求。一些基本的性能指标如下:
- 吞吐量(throughput)
- 延迟(latency)
- 容量(capacity)
- 利用率(utilization)
- 效率(efficiency)
- 可扩展性(scalability)
- 降级(degradation)
下面将依次简要地探讨这些指标。注意,对大部分性能项目而言,并非要同时优化每个指标。在每次性能迭代中只改进几个指标的情况更为常见,而且一次可以调优的指标可能也就这么多。在实际项目中,很可能会出现这样的情况:优化一个指标可能会损害另外一个或几个指标。
⚾️2.1. 吞吐量
吞吐量是一个表示系统或子系统能够执行的工作速率的指标,通常用某个时间段内的工作单元数来表示。比如,我们可能会对系统每秒能执行多少个事务感兴趣。
要使吞吐量数字在实际的性能实践中具有意义,那它应该包括对获得该数据的参考平台的描述。比如,硬件规格、操作系统和软件栈都与吞吐量有关;被测试的系统是单台服务器还是集群也是如此。此外,不同测试之间的事务数(或工作单元)应该是相同的。基本上,我们应该设法确保进行吞吐量测试的工作负载在多次运行之间保持一致。
⚾️2.2. 延迟
有时可以把性能指标比喻为水管,以此来解释它。如果一根水管每秒能流出 100 升水,那么 1 秒内流出的水量(100 升)就是吞吐量。在这个比喻中,延迟实际上就是水管的长度。
也就是说,它是处理一个事务并在水管的另一端看到结果所花费的时间。
它通常被称为端到端的时间。
⚾️2.3. 容量
容量是指系统所拥有的工作并行度,即系统中可以同时进行的工作单元(比如事务)的数量。
容量显然与吞吐量有关,而且我们应该可以预料到,随着系统上并发负载的增加,吞吐量(和延迟)会受到影响。因此,容量通常被看作在给定的延迟或吞吐量值下可用的处理能力。
⚾️2.4. 利用率
最常见的性能分析任务之一就是实现系统资源的高效利用。理想情况下, CPU 应该用于处理各个工作单元,而不是闲置(或者花时间处理操作系统或其他管理任务)。
根据工作负载的不同,不同资源的利用水平可能差异巨大。比如,在计算密集型的工作负载(如图形处理或加密)运行的时候, CPU 利用率可能接近 100%,但只使用了一小部分可用内存。
⚾️2.5. 效率
将系统的吞吐量除以所使用的资源可以衡量系统的整体效率。直观上看,这是有道理的,因为要获得同样的吞吐量,需要更多资源的那个系统可以被定义为效率低下。
在处理较大的系统时,也可以使用成本核算的形式来衡量效率。如果用美元计算,解决方案 A 的总体拥有成本(TCO)是解决方案 B 的两倍,那么在相同的吞吐量下, A 的效率显然是 B 的一半。
⚾️2.6. 可扩展性
系统的吞吐量或容量取决于可用于处理的资源。添加资源时,吞吐量的变化是衡量系统或应用程序可扩展性的一个标准。系统可扩展性的最理想情况是,吞吐量的变化和资源的变化步调一致。
考虑一个基于服务器集群的系统。如果集群被扩大,比如说规模扩大了一倍,那么可以实现什么样的吞吐量呢?如果新的集群可以处理两倍的事务量,那么该系统将呈现“完美的线性扩展”。这在实践中很难实现,特别是在面对各种可能的负载时。
系统的可扩展性取决于很多因素,而且通常不是一个简单的恒定因素。常见的情况是,对于某个范围内的资源,系统的可扩展性接近于线性,但在更高的负载下,系统会遇到一些阻碍完美扩展的限制。
⚾️2.7. 降级
如果我们通过增加请求(或客户端)的数量或加快请求到达的速度来增加系统的负载,可能会看到观察到的延迟或吞吐量的变化。
请注意,这种变化取决于系统的利用率。如果系统利用率不足,那么在出现可观测到的变化之前,系统应该比较平缓;但如果资源已经被充分利用,那么我们会看到吞吐量停止增加或延迟增加。这些变化通常被称为系统在额外负载下的降级。
👋小结
本文粗略介绍了Java性能是什么,不是什么;然后介绍了一个好的性能实践将用到的基本词汇。方便大家学习和参考。