在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)做性能测试的更多相关文章

  1. Java中数组操作 java.util.Arrays 类常用方法的使用

    任何一门编程语言,数组都是最重要和常用的数据结构之一,但不同的语言对数组的构造与处理是不尽相同的. Java中提供了java.util.Arrays 类能方便地操作数组,并且它提供的所有方法都是静态的 ...

  2. java中使用队列:java.util.Queue

    在java5中新添加了java.util.Queue接口,用以支持队列的常见操作.该接口扩展了java.util.Collection接口.Queue使用时要尽量避免Collection的add()和 ...

  3. Java中的static修饰int值做全局变量与static修饰词初始化顺序

    先看一道题 public class HasStatic{ private static int x=100; public static void main(String args[]){ HasS ...

  4. Java中的泛型 --- Java 编程思想

    前言 ​ 我一直都认为泛型是程序语言设计中一个非常基础,重要的概念,Java 中的泛型到底是怎么样的,为什么会有泛型,泛型怎么发展出来的.通透理解泛型是学好基础里面中非常重要的.于是,我对<Ja ...

  5. java中使用队列:java.util.Queue(转)

    队列是一种特殊的线性表,是运算受到限制的一种线性表,只允许在表的一端进行插入,而在另一端进行删除元素的线性表.队尾(rear)是允许插入的一端.队头(front)是允许删除的一端.空队列是不含元素的空 ...

  6. Java中的参数传递 --Java

    1.基本类型传值,对象类型传地址 按值传递:当将一个参数传递给一个方法时,方法接收的是原始值的一个副本.因此,如果方法修改了该参数,仅改变副本,而原始值保持不变. 按引用传递:当将一个参数传递给一个方 ...

  7. Java中的日志——Java.util.logging、log4j、commons-logging

    Java中给项目程序添加log主要有三种方式,一使用JDK中的java.util.logging包,一种是log4j,一种是commons-logging.其中log4j和commons-loggin ...

  8. 避免Java中NullPointerException的Java技巧和最佳实践

    Java中的NullPointerException是我们最经常遇到的异常了,那我们到底应该如何在编写代码是防患于未然呢.下面我们就从几个方面来入手,解决这个棘手的​问题吧.​ 值得庆幸的是,通过应用 ...

  9. java中使用队列:java.util.Queue (转)

    Queue接口与List.Set同一级别,都是继承了Collection接口.LinkedList实现了Queue接 口.Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类 ...

随机推荐

  1. 看完这篇Exception 和 Error,和面试官扯皮就没问题了

    在 Java 中的基本理念是 结构不佳的代码不能运行,发现错误的理想时期是在编译期间,因为你不用运行程序,只是凭借着对 Java 基本理念的理解就能发现问题.但是编译期并不能找出所有的问题,有一些 N ...

  2. Git版本控制 Git、github,gitlab相关操作

    目录 关于版本控制 版本管理工具 集中式管理 分布式管理 git版本管理 git介绍 软件安装 Git工作状态 原理流程步骤 git基本操作 对文件进行修改 分支 共享仓库 创建共享仓库: 共享仓库上 ...

  3. Linux命令 dos2unix 的实际应用场景

    问题描述:书接上文springboot之停止与启动服务的脚本,编写杀死服务脚本的时候,总是不行,Linux会提示你类似下面这样的信息: killed pid: : arguments must be ...

  4. Java中的数据结构-HashMap

    Java数据结构-HashMap 目录 Java数据结构-HashMap 1. HashMap 1.1 HashMap介绍 1.1.1 HashMap介绍 1.1.2 HashMap继承图 1.2 H ...

  5. shell查询目标jvm的perm占比

    #查询指定进程号下面的方法区使用率,jdk1.7是perm,jdk1.8是metaspace function get_perm_use_percent() { pid="$1" ...

  6. 从一个慢查询到MySQL字符集编码

    从一个慢查询到MySQL字符集编码 目录 从一个慢查询到MySQL字符集编码 1. 问题起源 2. MySQL字符集和字符集排序规则 2.1 字符集相关概念 2.2 MySQL中的字符集和字符集排序规 ...

  7. 中阶 d06 http协议与servlet

    * 什么是协议 > 双方在交互.通讯的时候, 遵守的一种规范.规则. * http协议 > 针对网络上的客户端 与 服务器端在执行http请求的时候,遵守的一种规范. 其实就是规定了客户端 ...

  8. 22 Extends 继承(子类、父类)

    本章主要介绍继承的 概念.方法重写(@Override注解的使用).使用场景.方法的执行顺序 /*1.继承的 概念 * 继承:多个类有共同的成员变量和成员方法,抽取到另外一个类中(父类),在让多个类去 ...

  9. Struts2-学习笔记系列(2)-常量配置和实现action

    1.常量配置 在struts配置文件中,下面突出部分,就是常量配置. <constant name="struts.enable.DynamicMethodInvocation&quo ...

  10. python3(二十五) getClassInfo

    """ """ __author__ = 'shaozhiqi' # 如何知道这个对象是什么类型,使用type() print(type(1 ...