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 基本题目内…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...



