在java中使用JMH(Java Microbenchmark Harness)做性能测试
在java中使用JMH(Java Microbenchmark Harness)做性能测试
JMH的全称是Java Microbenchmark Harness,是一个open JDK中用来做性能测试的套件。该套件已经被包含在了JDK 12中。
本文将会讲解如何使用JMH来在java中做性能测试。
如果你使用的不是JDK 12,那么需要添加如下依赖:
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.19</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.19</version>
</dependency>
使用JMH做性能测试
如果我们想测试某个方法的性能,一般来说就是重复执行某个方法n次,求出总的执行时间,然后求平均值。
但是这样通常会有一些问题,比如程序的头几次执行通常会比较慢,因为JVM会对多次执行的代码进行优化。另外得出的统计结果也不够直观,需要我们自行解析。
如果使用JMH可以轻松解决这些问题。
在JMH中,将要测试的方法添加@Benchmark注解即可:
@Benchmark
public void measureThroughput() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(100);
}
看下怎么调用:
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(BenchMarkUsage.class.getSimpleName())
// .include(BenchMarkUsage.class.getSimpleName()+".*measureThroughput*")
// 预热3轮
.warmupIterations(3)
// 度量5轮
.measurementIterations(5)
.forks(1)
.build();
new Runner(opt).run();
}
上面的例子,我们通过OptionsBuilder的include方法添加了需要进行测试的类。
默认情况下,该类的所有@Benchmark方法都将会被测试,如果我们只想测试其中的某个方法,我们可以在类后面加上方法的名字:
.include(BenchMarkUsage.class.getSimpleName()+".*measureAll*")
上面的代码支持通配符。
warmupIterations(3)意思是在真正的执行前,先热身三次。
measurementIterations(5)表示我们将方法运行5次来测试性能。
forks(1)表示启动一个进程来执行这个任务。
上面是最基本的运行,我们看下运行结果:
# JMH version: 1.19
# VM version: JDK 1.8.0_171, VM 25.171-b11
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA 2.app/Contents/lib/idea_rt.jar=55941:/Applications/IntelliJ IDEA 2.app/Contents/bin -Dfile.encoding=UTF-8
# Warmup: 3 iterations, 1 s each
# Measurement: 5 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.flydean.BenchMarkUsage.measureThroughput
# Run progress: 26.66% complete, ETA 00:01:42
# Fork: 1 of 1
# Warmup Iteration 1: 9.727 ops/s
# Warmup Iteration 2: 9.684 ops/s
# Warmup Iteration 3: 9.678 ops/s
Iteration 1: 9.652 ops/s
Iteration 2: 9.678 ops/s
Iteration 3: 9.733 ops/s
Iteration 4: 9.651 ops/s
Iteration 5: 9.678 ops/s
Result "com.flydean.BenchMarkUsage.measureThroughput":
9.678 ±(99.9%) 0.129 ops/s [Average]
(min, avg, max) = (9.651, 9.678, 9.733), stdev = 0.034
CI (99.9%): [9.549, 9.808] (assumes normal distribution)
ops/s 是每秒的OPS次数。程序会给出运行的最小值,平均值和最大值。同时给出标准差stdev和置信区间CI。
BenchmarkMode
上面的例子中, 我们只用了最简单的@Benchmark。如果想实现更加复杂和自定义的BenchMark,我们可以使用@BenchmarkMode。
先举个例子:
@Benchmark
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public void measureThroughput() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(100);
}
上面的例子中,我们指定了@BenchmarkMode(Mode.Throughput),Throughput的意思是整体吞吐量,表示给定的时间内执行的次数。
这里我们通过 @OutputTimeUnit(TimeUnit.SECONDS)来指定时间单位。
Mode除了Throughput还有如下几种模式:
- AverageTime - 调用的平均时间
- SampleTime - 随机取样,最后输出取样结果的分布
- SingleShotTime - 只会执行一次,通常用来测试冷启动时候的性能。
- All - 所有的benchmark modes。
Fork和Warmup
上面的例子中我们通过代码来显式的制定Fork和Warmup,我们也可以使用注解来实现:
@Fork(value = 1, warmups = 2)
@Warmup(iterations = 5)
上面的例子中value表示该benchMark执行多少次,warmups表示fork多少个进程来执行。iterations表示warmup的iterations个数。
如果你同时在代码中和注解中都配置了相关的信息,那么注解将会覆盖掉代码中的显示配置。
State和Scope
如果我们在多线程环境中使用beachMark,那么多线程中用到的类变量是共享还是每个线程一个呢?
这个时候我们就要用到@State注解。
@State(Scope.Benchmark)
public class StateUsage {
}
Scope有三种:
- Scope.Thread:默认的State,每个测试线程分配一个实例;
- Scope.Benchmark:所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能;
- Scope.Group:每个线程组共享一个实例;
本文的例子可以参考https://github.com/ddean2009/learn-java-concurrency/tree/master/benchmark
更多教程请参考 flydean的博客
在java中使用JMH(Java Microbenchmark Harness)做性能测试的更多相关文章
- Java中数组操作 java.util.Arrays 类常用方法的使用
任何一门编程语言,数组都是最重要和常用的数据结构之一,但不同的语言对数组的构造与处理是不尽相同的. Java中提供了java.util.Arrays 类能方便地操作数组,并且它提供的所有方法都是静态的 ...
- java中使用队列:java.util.Queue
在java5中新添加了java.util.Queue接口,用以支持队列的常见操作.该接口扩展了java.util.Collection接口.Queue使用时要尽量避免Collection的add()和 ...
- Java中的static修饰int值做全局变量与static修饰词初始化顺序
先看一道题 public class HasStatic{ private static int x=100; public static void main(String args[]){ HasS ...
- Java中的泛型 --- Java 编程思想
前言 我一直都认为泛型是程序语言设计中一个非常基础,重要的概念,Java 中的泛型到底是怎么样的,为什么会有泛型,泛型怎么发展出来的.通透理解泛型是学好基础里面中非常重要的.于是,我对<Ja ...
- java中使用队列:java.util.Queue(转)
队列是一种特殊的线性表,是运算受到限制的一种线性表,只允许在表的一端进行插入,而在另一端进行删除元素的线性表.队尾(rear)是允许插入的一端.队头(front)是允许删除的一端.空队列是不含元素的空 ...
- Java中的参数传递 --Java
1.基本类型传值,对象类型传地址 按值传递:当将一个参数传递给一个方法时,方法接收的是原始值的一个副本.因此,如果方法修改了该参数,仅改变副本,而原始值保持不变. 按引用传递:当将一个参数传递给一个方 ...
- Java中的日志——Java.util.logging、log4j、commons-logging
Java中给项目程序添加log主要有三种方式,一使用JDK中的java.util.logging包,一种是log4j,一种是commons-logging.其中log4j和commons-loggin ...
- 避免Java中NullPointerException的Java技巧和最佳实践
Java中的NullPointerException是我们最经常遇到的异常了,那我们到底应该如何在编写代码是防患于未然呢.下面我们就从几个方面来入手,解决这个棘手的问题吧. 值得庆幸的是,通过应用 ...
- java中使用队列:java.util.Queue (转)
Queue接口与List.Set同一级别,都是继承了Collection接口.LinkedList实现了Queue接 口.Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类 ...
随机推荐
- Python常用模块之configparser
ConfigParser简介 ConfigParser 是用来读取配置文件的包.配置文件的格式如下:中括号“[ ]”内包含的为section.section 下面为类似于key-value 的配置内容 ...
- 物体的三维识别与6D位姿估计:PPF系列论文介绍(三)
作者:仲夏夜之星 Date:2020-04-08 来源:物体的三维识别与6D位姿估计:PPF系列论文介绍(三) 文章“A Method for 6D Pose Estimation of Free-F ...
- nginx 安装教程
Nginx 安装教程 本教程在CentOS6.7中安装nginx 1.8.0,Nginx中加入了taobao的concat模块,nginx-upload-module模块,pcre 以及nginx-u ...
- time_wait 详解和解决方案
1. 产生原因 2. 导致问题 3. Nginx 3.1 长连接 4. 解决方案 5 .参考 产生原因 TCP 连接关闭时,会有 4 次通讯(四次挥手),来确认双方都停止收发数据了.如上图,主动关闭方 ...
- Vulnhub DC-6靶机渗透
信息搜集 nmap -sP 192.168.146.0/24 #找靶机ip nmap -sS -Pn -A 192.168.146.143 #扫描靶机信息 22和80端口,老朋友了. 先直接访问htt ...
- Wirte-up:攻防世界Web解题过程新手区01-06
文章更新于:2020-02-18 说明:为了标识图片边界,有些图片加了红线以增强观感. 注1: web 环境搭建参见: Windows&linux使用集成环境搭建 web 服务器 注2:DVW ...
- Linux学习,账号管理与权限管理
linux系统本来不认识账号,只是通过UID(用户ID)和GID(所属组ID)来区分账号属性的.而这对应的目录如下: UID ===> /etc/passwd GID ===> /etc/ ...
- Tcl编成第二天,set与unset
代码如下: #!/usr/bin/tclsh set value "one" puts $value unset value puts $value set表示创建一个变量第一个参 ...
- MyBatis-Plus使用小结
官网: https://mybatis.plus/ https://gitee.com/baomidou/mybatis-plus https://github.com/baomidou/mybati ...
- sublime text3配置html环境
1.安装View in Browser 2.配置快捷键 [1]Preferences—Key Bindings—User. [2]插入代码 [ //ie { "keys": [&q ...