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 基本题目内…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...

数据分析六部曲?
引言 上一章我们说到了数据分析六部曲,何谓六部曲呢? 其实啊,数据分析没那么难,只要掌握了下面这六个步骤,也就是数据分析六部曲,就算你是个啥都不懂的小白,也能慢慢上手做数据分析啦。 第一…...
Qt学习及使用_第1部分_认识Qt---Qt开发基本流程
前言 学以致用,通过QT框架的学习,一边实践,一边探索编程的方方面面. 参考书:<Qt 6 C开发指南>(以下称"本书") 标识说明:概念用粗体倾斜.重点内容用(加粗黑体)---重点内容(红字)---重点内容(加粗红字), 本书原话内容用深蓝色标识,比较重要的内容用加粗倾…...
Linux信号保存与处理机制详解
Linux信号的保存与处理涉及多个关键机制,以下是详细的总结: 1. 信号的保存 进程描述符(task_struct):每个进程的PCB中包含信号相关信息。 pending信号集:记录已到达但未处理的信号(未决信号&a…...