Java性能调优杀手锏JMH
JMH简介
JMH(Java Microbenchmark Harness)由 OpenJDK/Oracle 里面那群开发了 Java编译器的大牛们所开发,是一个功能强大、灵活的工具,它可以用于检测和评估Java应用程序的性能,主要目的是测量Java应用程序的性能,尤其是在多线程环境下的性能。它使用自动生成的测试用例,来测量应用程序的性能,而不是简单的测量方法的性能。
为什么说是微基准测试工具(Micro Benchmark) 呢?
因为是使用在method 层面上,精度可以精确到微秒级。
应用场景:
1、想准确地知道某个方法需要执行多长时间,以及执行时间和输入之间的相关性
2、对比一个函数不同实现在给定条件下的吞吐量(例如 List接口有ArrayList和LinkedList实现),不知道哪种实现性能更好
3、对热点函数进行进一步的优化时,可以使用 JMH 对优化的效果进行定量的分析
快速使用
依赖:
<dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-core</artifactId><version>1.27</version></dependency><dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-generator-annprocess</artifactId><version>1.27</version>
</dependency>
编写基准测试
测试String+ 和 StringBuilder.append() 两种字符串拼接哪个耗时更短,具体代码如下所示:
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.*;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;import java.util.concurrent.TimeUnit;@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 3)
@Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS)
@Threads(8)
@Fork(2)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class TestStringBenchmark {@Benchmarkpublic void string() {String s = "";for (int i = 0; i < 10; i++) {s += i;}}@Benchmarkpublic void stringBuilder() {StringBuilder sb = new StringBuilder();for (int i = 0; i < 10; i++) {sb.append(i);}}public static void main(String[] args) throws RunnerException {Options options = new OptionsBuilder().include(TestStringBenchmark.class.getSimpleName()).output("D:/benchmark.log").result("D:/jmh_result.json").resultFormat(ResultFormatType.JSON).build();new Runner(options).run();}}
TestStringBenchmark
# JMH version: 1.27
# VM version: JDK 1.8.0_91, Java HotSpot(TM) 64-Bit Server VM, 25.91-b15
# VM invoker: F:\Java\jdk1.8.0_91\jre\bin\java.exe
# VM options: -Dvisualvm.id=1830274449076800 -javaagent:F:\IntelliJ IDEA 2021.1.2\lib\idea_rt.jar=54701:F:\IntelliJ IDEA 2021.1.2\bin -Dfile.encoding=UTF-8
# JMH blackhole mode: full blackhole + dont-inline hint
# Warmup: 3 iterations, 10 s each
# Measurement: 10 iterations, 5 s each
# Timeout: 10 min per iteration
# Threads: 8 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: TestStringBenchmark.string# Run progress: 0.00% complete, ETA 00:05:20
# Fork: 1 of 2
# Warmup Iteration 1: 11945.156 ops/ms
# Warmup Iteration 2: 15712.330 ops/ms
# Warmup Iteration 3: 14511.393 ops/ms
Iteration 1: 14799.601 ops/ms
Iteration 2: 14200.953 ops/ms
Iteration 3: 15198.794 ops/ms
Iteration 4: 14358.358 ops/ms
Iteration 5: 14782.530 ops/ms
Iteration 6: 14986.920 ops/ms
Iteration 7: 15457.126 ops/ms
Iteration 8: 14530.070 ops/ms
Iteration 9: 14836.293 ops/ms
Iteration 10: 14289.823 ops/ms# Run progress: 25.00% complete, ETA 00:04:12
# Fork: 2 of 2
# Warmup Iteration 1: 13425.736 ops/ms
# Warmup Iteration 2: 14322.458 ops/ms
# Warmup Iteration 3: 15806.225 ops/ms
Iteration 1: 16404.253 ops/ms
Iteration 2: 16489.533 ops/ms
Iteration 3: 15394.679 ops/ms
Iteration 4: 16249.505 ops/ms
Iteration 5: 16780.925 ops/ms
Iteration 6: 16232.516 ops/ms
Iteration 7: 15428.030 ops/ms
Iteration 8: 15868.765 ops/ms
Iteration 9: 15610.369 ops/ms
Iteration 10: 15852.798 ops/msResult "TestStringBenchmark.string":15387.592 ±(99.9%) 683.453 ops/ms [Average](min, avg, max) = (14200.953, 15387.592, 16780.925), stdev = 787.065CI (99.9%): [14704.139, 16071.045] (assumes normal distribution)# JMH version: 1.27
# VM version: JDK 1.8.0_91, Java HotSpot(TM) 64-Bit Server VM, 25.91-b15
# VM invoker: F:\Java\jdk1.8.0_91\jre\bin\java.exe
# VM options: -Dvisualvm.id=1830274449076800 -javaagent:F:\IntelliJ IDEA 2021.1.2\lib\idea_rt.jar=54701:F:\IntelliJ IDEA 2021.1.2\bin -Dfile.encoding=UTF-8
# JMH blackhole mode: full blackhole + dont-inline hint
# Warmup: 3 iterations, 10 s each
# Measurement: 10 iterations, 5 s each
# Timeout: 10 min per iteration
# Threads: 8 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: TestStringBenchmark.stringBuilder# Run progress: 50.00% complete, ETA 00:02:47
# Fork: 1 of 2
# Warmup Iteration 1: 92947.764 ops/ms
# Warmup Iteration 2: 44994.076 ops/ms
# Warmup Iteration 3: 44235.632 ops/ms
Iteration 1: 34708.010 ops/ms
Iteration 2: 33766.341 ops/ms
Iteration 3: 28813.465 ops/ms
Iteration 4: 30891.785 ops/ms
Iteration 5: 38159.050 ops/ms
Iteration 6: 38445.872 ops/ms
Iteration 7: 40228.793 ops/ms
Iteration 8: 43060.997 ops/ms
Iteration 9: 40186.636 ops/ms
Iteration 10: 42147.155 ops/ms# Run progress: 75.00% complete, ETA 00:01:23
# Fork: 2 of 2
# Warmup Iteration 1: 84987.092 ops/ms
# Warmup Iteration 2: 43920.167 ops/ms
# Warmup Iteration 3: 48326.564 ops/ms
Iteration 1: 44952.072 ops/ms
Iteration 2: 46982.241 ops/ms
Iteration 3: 41305.765 ops/ms
Iteration 4: 44203.234 ops/ms
Iteration 5: 47615.004 ops/ms
Iteration 6: 45715.135 ops/ms
Iteration 7: 47256.792 ops/ms
Iteration 8: 45201.375 ops/ms
Iteration 9: 47769.656 ops/ms
Iteration 10: 48446.133 ops/msResult "TestStringBenchmark.stringBuilder":41492.776 ±(99.9%) 5059.358 ops/ms [Average](min, avg, max) = (28813.465, 41492.776, 48446.133), stdev = 5826.364CI (99.9%): [36433.418, 46552.133] (assumes normal distribution)# Run complete. Total time: 00:05:34REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.Benchmark Mode Cnt Score Error Units
TestStringBenchmark.string thrpt 20 15387.592 ± 683.453 ops/ms
TestStringBenchmark.stringBuilder thrpt 20 41492.776 ± 5059.358 ops/msBenchmark result is saved to D:/jmh_resultProcess finished with exit code 0
根据测试结果说明,在拼接字符次数越多的情况下,StringBuilder.append() 的性能明显更优秀。
jar 包执行
对于一些小测试,直接用上面的方式写一个 main 函数手动执行就好了。
对于大型的测试,需要测试的时间比较久、线程数比较多,加上测试的服务器需要,一般要放在 Linux 服务器里去执行。
JMH 官方提供了生成 jar 包的方式来执行。
第一步: maven 里增加一个 plugin,具体配置如下:
<plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>2.4.1</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><finalName>jmh-demo</finalName><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>org.openjdk.jmh.Main</mainClass></transformer></transformers></configuration></execution></executions></plugin>
</plugins>
或执行
mvn archetype:generate \-DinteractiveMode=false \-DarchetypeGroupId=org.openjdk.jmh \-DarchetypeArtifactId=jmh-java-benchmark-archetype \-DgroupId=org.sample \-DartifactId=test \-Dversion=1.0
第二步:执行 maven 的命令生成可执行 jar 包并执行:
mvn clean install
java -jar target/jmh-demo.jar TestStringBenchmark
IDEA安装JMH 插件
在 IDEA 中点击 File->Settings…->Plugins

JMH插件与JUnit 有相同的使用方式,主要功能如下:
自动生成带有 @Benchmark 的方法
像 JUnit 一样,运行单独的 Benchmark 方法
运行类中所有的Benchmark 方法
使用右键点击 Generate

JMH可视化
图形化网站:
将json 文件导入,就可以实现可视化。
JMH Visual Chart:
http://deepoove.com/jmh-visual-chart/
JMH Visualizer:
https://jmh.morethan.io/
注解
@BenchmarkMode
用来配置 Mode 选项,可用于类或者方法上,这个注解的 value 是一个数组,可以把几种 Mode 集合在一起执行,如:@BenchmarkMode({Mode.SampleTime, Mode.AverageTime}),还可以设置为 Mode.All,即全部执行一遍。
Throughput:整体吞吐量,每秒执行了多少次调用,单位为 ops/time
AverageTime:用的平均时间,每次操作的平均时间,单位为 time/op
SampleTime:随机取样,最后输出取样结果的分布
SingleShotTime:只运行一次,往往同时把 Warmup 次数设为 0,用于测试冷启动时的性能
All:上面的所有模式都执行一次
@State
通过 State 可以指定一个对象的作用范围,JMH 根据 scope 来进行实例化和共享操作。@State 可以被继承使用,如果父类定义了该注解,子类则无需定义。由于 JMH 允许多线程同时执行测试,不同的选项含义如下:
Scope.Benchmark:所有测试线程共享一个实例,测试有状态实例在多线程共享下的性能
Scope.Group:同一个线程在同一个 group 里共享实例
Scope.Thread:默认的 State,每个测试线程分配一个实例
@OutputTimeUnit
为统计结果的时间单位,可用于类或者方法注解
@Warmup
预热所需要配置的一些基本测试参数,可用于类或者方法上。一般前几次进行程序测试的时候都会比较慢,所以要让程序进行几轮预热,保证测试的准确性。参数如下所示:
iterations:预热的次数
time:每次预热的时间
timeUnit:时间的单位,默认秒
batchSize:批处理大小,每次操作调用几次方法
为什么需要预热?
因为 JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM 会尝试将其编译为机器码,从而提高执行速度,所以为了让
benchmark 的结果更加接近真实情况就需要进行预热。
@Measurement
实际调用方法所需要配置的一些基本测试参数,可用于类或者方法上,参数和 @Warmup 相同。
@Threads
每个进程中的测试线程,可用于类或者方法上。
@Fork
进行 fork 的次数,可用于类或者方法上。如果 fork 数是 2 的话,则 JMH 会 fork 出两个进程来进行测试。
@Param
指定某项参数的多种情况,特别适合用来测试一个函数在不同的参数输入的情况下的性能,只能作用在字段上,使用该注解必须定义@State 注解。
小结
JMH是一款强大的Java和JVM性能基准测试工具,它能够准确、灵活地测量各种Java应用程序的性能,并通过可视化界面帮助开发人员快速定位性能瓶颈。有了JMH,开发人员就能够更加精确、有效地调优Java应用程序的性能,从而提高应用程序的效率和稳定性。
参考资料
https://www.oracle.com/technical-resources/articles/java/architect-benchmarking.html
https://github.com/lexburner/JMH-samples
https://www.cnkirito.moe/java-jmh/
https://openjdk.org/projects/code-tools/jmh/

相关文章:
Java性能调优杀手锏JMH
JMH简介 JMH(Java Microbenchmark Harness)由 OpenJDK/Oracle 里面那群开发了 Java编译器的大牛们所开发,是一个功能强大、灵活的工具,它可以用于检测和评估Java应用程序的性能,主要目的是测量Java应用程序的性能,尤其是在多线程…...
实现excle表上传生成echarts图
代码如下html <!--这是一个网上关于读取Excel最经典的代码--> <!DOCTYPE html> <html><head><meta charset"utf-8"><title>ECharts</title><!-- 引入 echarts.js --><!-- <script src"newjs/js/incubato…...
python代码如何打包
网上的文章对小白都不太友好呀,讲得都比较高大上,本文章就用最简单的方式来教会大家如何打包。既然各位已经学习到了python打包了, 深适度应该跟我查不多。 注意事项: 1. 这个插件只能打包 mac 、win系统运行的文件,也…...
MyBatis学习笔记(十二) —— MyBatis的逆向工程
12、MyBatis的逆向工程 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的。 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源: Java实体类Mappe…...
4.Elasticsearch深入了解
4.Elasticsearch深入了解[toc]1.Elasticsearch架构原理Elasticsearch的节点类型在Elasticsearch主要分成两类节点,一类是Master,一类是DataNode。Master节点在Elasticsearch启动时,会选举出来一个Master节点。当某个节点启动后,然…...
【HashSet】| 深度剥析Java SE 源码合集Ⅲ
目录一. 🦁 HashSet介绍1.1 特点1.2 底层实现二. 🦁 结构以及对应方法分析2.1 结构组成2.1.1 源码实现2.1.2 成员变量及构造方法2.2 常用的方法2.2.1 添加add(E e)方法2.2.2 删除remove(Object o)方法三. 最后想说一. 🦁 HashSet介绍 1.1 特…...
你了解线程的状态转换吗
本文概述: 讲述线程的六种状态. 你可能已经了解了六种状态, 但是你知道 sleep 被唤醒之后, wait ()被 notify 之后进入了什么状态吗? 本文只是开胃小菜, 你看看下一篇文章对你有没有帮助. 一共有六种状态: New 新建状态Runnable 运行状态Blocked 阻塞状态Waiting 等待状态Tim…...
MyBatis-Plus联表查询的短板,该如何解决呢
mybatis-plus作为mybatis的增强工具,它的出现极大的简化了开发中的数据库操作,但是长久以来,它的联表查询能力一直被大家所诟病。一旦遇到left join或right join的左右连接,你还是得老老实实的打开xml文件,手写上一大段…...
吲哚菁绿-巯基,ICG-SH,科研级别试剂,吲哚菁绿可用于测定心输出量、肝脏功能、肝血流量,和对于眼科血管造影术。
ICG-THIOL,吲哚菁绿-巯基 中文名称:吲哚菁绿-巯基 英文名称:ICG-THIOL 英文别名:ICG-SH 性状:绿色粉末 溶剂:溶于二氯甲烷等其他常规有机溶剂 稳定性:冷藏保存,避免反复冻融。 存储条件&…...
深度剖析JavaOptional类
Java Optional 类 Optional类在 Java 8中被加了进来,提供了一种处理业务逻辑想要的值可能没有出现(null)也可能出现的情况,可能直到目前,我们还是用null 来表示业务值不存在的情况,但是这可能导致空指针异常,Java 8新添加 Optional类可以从一定程度上来解决这个问题。 O…...
平面设计软件Corel CDR2023又开始放大招啦,CorelDRAW Graphics Suite 2023有哪些新增功能?
CorelDRAW 2023中文版即将于2023年3月14日,在苏州举行线上直播的2023新品发布会,本次发布会主题为“设计新生力,矢量新未来”。 发布会邀请思杰马克丁公司领导、Corel 中国区总经理分享思杰与 Corel 的合作模式及在 CorelDRAW 产品上推动历程…...
初学torch【报错:expected scalar type double but found float、rmse】
目录 一、inout 二、expected scalar type double but found float 报错 三、pytorch中回归评价rmse: 一、inout torch网络训练,输入需要转换为tensor格式: import torch import numpy A torch.arange(12, dtypetorch.float32).reshape((…...
金三银四、金九银十 面试宝典 JAVASE八股文面试题 超级无敌全的面试题汇总(接近3万字的面试题,让你的JAVA语法基础无可挑剔)
JavaSE八股文 - 面试宝典 一个合格的 计算机打工人 ,收藏夹里必须有一份 JAVA八股文面试题 ,特别是即将找工作的计算机人,希望本篇博客对你有帮助! 本文参考了诸多大佬的面试题帖子,ps:白大锅、哪吒、英雄…...
数据结构:链式二叉树初阶
目录 一.链式二叉树的逻辑结构 1.链式二叉树的结点结构体定义 2.链式二叉树逻辑结构 二.链式二叉树的遍历算法 1.前序遍历 2.中序遍历 3.后序遍历 4.层序遍历(二叉树非递归遍历算法) 层序遍历概念: 层序遍历算法实现思路: 层序遍历代码实现: 三.链式二叉树遍历算…...
公式编写1000问9-12
9.问: 买入:日线创100日新高 ,周线(5周)BIAS>10 卖出:2日收盘在30线下方 注:买卖都只要单一信号即可,不要连续给出信号 我今天才开始学习编写,可是没有买入信号,不知道哪错了? B1…...
C++11:类的新功能和可变参数模板
文章目录1. 新增默认成员函数1.1 功能1.2 示例2. 类成员变量初始化3. 新关键字3.1 关键字default3.2 关键字delete补充3.3 关键字final和override4. 可变参数模板4.1 介绍4.2 定义方式4.3 展开参数包递归展开参数包优化初始化列表展开参数包逗号表达式展开参数包补充5. emplace…...
【Java学习笔记】15.Java 日期时间(1)
Java 日期时间 java.util 包提供了 Date 类来封装当前的日期和时间。 Date 类提供两个构造函数来实例化 Date 对象。 第一个构造函数使用当前日期和时间来初始化对象。 Date( )第二个构造函数接收一个参数,该参数是从 1970 年 1 月 1 日起的毫秒数。 Date(long …...
在ROS2中,通过MoveIt2控制Gazebo中的自定义机械手
目前的空余时间主要都在研究ROS2,最终目的是控制自己用舵机组装的机械手。 由于种种原因,先控制Gazebo的自定义机械手。 先看看目前的成果 左侧是rviz2中的moveit组件的机械手,右侧是gazebo中的机械手。在moveit中进行路径规划并执行后&#…...
Java-线程池 原子性 类
Java-线程池 原子性 类线程池构造方法调用Executors静态方法创建调用方法直接创建线程池对象原子性volatile-问题出现原因:volatile解决原子性AtomicInteger的常用方法悲观锁和乐观锁synchronized(悲)和CAS(乐)的区别并发工具类Hashtable集合ConcurrentHashMap原理:CountDownLa…...
力扣sql简单篇练习(二十五)
力扣sql简单篇练习(二十五) 1 无效的推文 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # Write your MySQL query statement below SELECT tweet_id FROM Tweets WHERE CHAR_LENGTH(content)>151.3 运行截图 2 求关注者的数量 2.1 基本题目内…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...



