概述

JMH 的全名是 Java Microbenchmark Harness,它是由 Java 虚拟机团队开发的一款用于 Java 微基准测试工具

依赖库

https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core

  1. <!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
  2. <dependency>
  3. <groupId>org.openjdk.jmh</groupId>
  4. <artifactId>jmh-core</artifactId>
  5. <version>1.35</version>
  6. </dependency>
  7. <!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess -->
  8. <dependency>
  9. <groupId>org.openjdk.jmh</groupId>
  10. <artifactId>jmh-generator-annprocess</artifactId>
  11. <version>1.35</version>
  12. <scope>provided</scope>
  13. </dependency>

出现Exception in thread “main” java.lang.RuntimeException: ERROR: Unable to find the resource: /META-INF/BenchmarkList问题,
问题就在于:jmh-generator-annprocess依赖的scope配置为test,此时表示该依赖仅仅在测试阶段会参与进来,包括测试代码的编译,执行。
由于这个依赖并不用于生产,我们可以将scope设置为provided

JMH注解

@BenchmarkMode

JMH使用@BenchmarkMode这个注解来声明运行模式,BenchmarkMode既可以在class上进行注解设置,也可以在基准方法上进行注解设置,方法中设置的模式将会覆盖class注解上的设置,同样,在Options中也可以进行设置,它将会覆盖所有基准方法上的设置。JMH为我们提供了四种运行模式:

AverageTime(平均响应时间) 要用于输出基准测试方法每调用一次所耗费的时间,也就是elapsed time/operation
Throughput(方法吞吐量) 则刚好与AverageTime相反,它的输出信息表明了在单位时间内可以对该方法调用多少次
SampleTime(时间采样) 采用一种抽样的方式来统计基准测试方法的性能结果,与我们常见的直方图几乎是一样的,它会收集所有的性能数据,并且将其分布在不同的区间中。
SingleShotTime 主要可用来进行冷测试,不论是Warmup还是Measurement,在每一个批次中基准测试方法只会被执行一次,一般情况下,我们会将Warmup的批次设置为0


@Benchmark

JMH对基准测试的方法需要使用@Benchmark注解进行标记,否则方法将被视为普通方法,并且不会对其执行基准测试

@Warmup

Warmup所做的就是在基准测试代码正式度量之前,先对其进行预热,使得代码的执行是经历过了类的早期优化、JVM运行期编译、JIT优化之后的最终状态,从而能够获得代码真实的性能数据。

参数说明

  • timeUnit:时间的单位,默认的单位是秒;
  • iterations:预热阶段的迭代数;
  • time:每次预热的时间;
  • batchSize:批处理大小,指定了每次操作调用几次方法。 ```java @Warmup(

iterations = 5,

time = 1,

timeUnit = TimeUnit.SECONDS)

  1. 代码预热总计 5 秒(迭代 5 次,每次一秒)。预热过程的测试数据,是不记录测量结果的。
  2. <a name="sUeKq"></a>
  3. ### @Measurement
  4. Measurement则是真正的度量操作,在每一轮的度量中,所有的度量数据会被纳入统计之中(预热数据不会纳入统计之中)
  5. ```java
  6. @Measurement(
  7. iterations = 5,
  8. time = 1,
  9. timeUnit = TimeUnit.SECONDS)

fork

fork 的值一般设置成 1,表示只使用一个进程进行测试;如果这个数字大于 1,表示会启用新的进程进行测试;但如果设置成 0,程序依然会运行,此时在用户的 JVM 进程上运行

Threads

fork 是面向进程的,而 Threads 是面向线程的。指定了这个注解以后,将会开启并行测试。如果配置了 Threads.MAX,则使用和处理机器核数相同的线程数。

@Setup

@Setup会在每一个基准测试方法执行前被调用,通常用于资源的初始化

@TearDown

@TearDown则会在基准测试方法被执行之后被调用,通常可用于资源的回收清理工作

  1. @BenchmarkMode(Mode.AverageTime)
  2. @State(Scope.Thread)
  3. @Fork(1)
  4. @OutputTimeUnit(TimeUnit.MILLISECONDS)
  5. @Warmup(iterations = 3)
  6. @Measurement(iterations = 5)
  7. public class StringBuildTest {
  8. String string = "";
  9. StringBuilder stringBuilder = new StringBuilder();
  10. @Benchmark
  11. public String stringAdd() {
  12. for (int i = 0; i < 1000; i++) {
  13. string = string + i;
  14. }
  15. return string;
  16. }
  17. @Benchmark
  18. public String stringBuilderAppend() {
  19. for (int i = 0; i < 1000; i++) {
  20. stringBuilder.append(i);
  21. }
  22. return stringBuilder.toString();
  23. }
  24. public static void main(String[] args) throws RunnerException {
  25. Options opt = new OptionsBuilder()
  26. .include(StringBuildTest.class.getSimpleName())
  27. .build();
  28. new Runner(opt).run();
  29. }
  30. }

连接池测试

使用连接池和不使用连接池,它们之间的性能差距到底有多大呢?下面是一个简单的 JMH 测试例子(见仓库),进行一个简单的 set 操作,为 redis 的 key 设置一个随机值。

  1. @Fork(2)
  2. @State(Scope.Benchmark)
  3. @Warmup(iterations = 5, time = 1)
  4. @Measurement(iterations = 5, time = 1)
  5. @BenchmarkMode(Mode.Throughput)
  6. public class JedisPoolVSJedisBenchmark {
  7. JedisPool pool = new JedisPool("localhost", 6379);
  8. @Benchmark
  9. public void testPool() {
  10. Jedis jedis = pool.getResource();
  11. jedis.set("a", UUID.randomUUID().toString());
  12. jedis.close();
  13. }
  14. @Benchmark
  15. public void testJedis() {
  16. Jedis jedis = new Jedis("localhost", 6379);
  17. jedis.set("a", UUID.randomUUID().toString());
  18. jedis.close();
  19. }
  20. ...

图形化

  1. Options opt = new OptionsBuilder()
  2. .resultFormat(ResultFormatType.JSON)
  3. .build();

JMH 支持 5 种格式结果

  • TEXT 导出文本文件。
  • CSV 导出 csv 格式文件。
  • SCSV 导出 scsv 等格式的文件。
  • JSON 导出成 json 文件。
  • LATEX 导出到 latex,一种基于 ΤΕΧ 的排版系统。

    图形工具

    JMH Visualizer

    https://jmh.morethan.io/
    通过导出 json 文件,上传至 JMH Visualizer

JMH Visual Chart
http://deepoove.com/jmh-visual-chart/

meta-chart
https://www.meta-chart.com/