在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. 使用jdbc实现简单的mvc模式的增删改查

    Mvc模式设计: 视图:添加界面(addUser.jsp),修改界面(updateUser.jsp),显示页面(allUser.jsp) 控制器:添加信息控制器(AddUserServlet),修改信 ...

  2. MySQL服务使用cmd启动与停止服务

    MySQL未设置自动启动,在使用时需要手动打开服务,方法如下 mysql服务的启动: 以管理员的身份运行cmd命令窗口,输入命名 net start mysql 提示:必须使用管理员身份运行cmd 如 ...

  3. 在docker中部署redis主从配置

    环境说明: 阿里云服务器 Ubuntu 16.04 docker 1.拉取Redis镜像 docker pull redis 2.配置Redis启动配置文件,此处我创建一个专用目录,存放Redis相关 ...

  4. Java 使用InputStream笔记

    当我们要从网络下载资源时,使用类似如下方法来获取InputStream实例: URLConnection connection = new URL("http://www.XXXX.XXX& ...

  5. 活久见!Linux命令行居然也可以用来查看图像?

    在 Linux 中有很多 GUI 应用程序可以查看图像,但是这对经常使用命令行来工作的人可能会觉得很繁琐.今天要介绍的是 3 个实用的 CLI 图像查看器来在终端上查看图像,让那些使用 CLI 的朋友 ...

  6. flask-script的基本使用

    Flask-Script flask-script的作用是可以通过命令行的形式来操作Flask.例如通过命令跑一个开发的服务器.设置数据库等. 命令的添加方式 1 .使用manage.command: ...

  7. Linux网络安全篇,配置Yum源(一),本地Yum源

    1.创建挂载目录 mkdir /mnt/cdrom 2.挂载软件源cdrom mount /dev/cdrom /mnt/cdrom/ 3.建立本地yum源资源文件夹 mkdir /usr/local ...

  8. C语言实现双向链表

    目前我们所学到的链表,无论是动态链表还是静态链表,表中各节点中都只包含一个指针(游标),且都统一指向直接后继节点,通常称这类链表为单向链表(或单链表). 虽然使用单链表能 100% 解决逻辑关系为 & ...

  9. MySQL中information_schema 数据库 是干什么的

    MySQL中information_schema是什么 大家在安装或使用MYSQL时,会发现除了自己安装的数据库以外,还有一个information_schema数据库. information_sc ...

  10. 如何提高你使用windows的逼格(windows用成Linux的赶脚)

    一.准备工作 作为一个整洁而有内涵的人,电脑桌面一定要清洁 二.桌面整洁了,软件怎么打开呢?     方案一 方案二.敲重点   我们可以使用终端指令打开windows安装的任意软件: 打开Windo ...