JMH ( Java MicroBenchmark Harness,JAVA 微基准测试套件 ) , 专门用于代码微基准测试的工具套件, 是基于方法层面的基准测试,精度可以达到微秒级。当定位到热点方法,并进一步优化方法性能的时候,就可以使用JMH对优化的结果进行量化的分析。
[应用场景]
:准确的知道某个方法需要执行多长时间,以及执行时间和输入之间的相关性
:对比接口不同实现在给定条件下的吞吐量,找到最优实现
:查看多少百分比的请求在多长时间内完成
[基本概念]
:Mode , JMH 进行 Benchmark 时所使用的模式。通常是测量的维度不同,或是测量的方式不同。
# Throughput , 整体吞吐量,例如“1秒内可以执行多少次调用”
# AverageTime, 调用的平均时间,例如“每次调用平均耗时xxx毫秒”
# SampleTime, 随机取样,最后输出取样结果的分布
# SingleShotTime, 以上模式都是默认一次 iteration 是 1s,唯有 SingleShotTime 是只运行一次。
往往同时把 warmup 次数设为0,用于测试冷启动时的性能
:Iteration , JMH 进行测试的最小单位。在大部分模式下,一次 iteration 代表的是一秒。
JMH 会在这一秒内不断调用需要 benchmark 的方法,然后根据模式对其采样,计算吞吐量,计算平均执行时间
:Warmup , 实际进行 benchmark 前先进行预热的行为
# JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM 会尝试将其编译成为机器码从而提高执行速度
[注解]
:@BenchmarkMode , 类和方法注解,配置Benchmark时所使用的模式
:@State , 类注解,JMH测试类必须使用@State注解,State定义了一个类实例的生命周期。
# JMH允许多线程同时执行测试
-> Scope.Thread , 默认的State,每个测试线程分配一个实例
-> Scope.Benchmark , 所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能
-> Scope.Group , 每个线程组共享一个实例
:@OutputTimeUnit , benchmark结果所使用的时间单位,java.util.concurrent.TimeUnit标准时间单位
:@Benchmark , 方法注解,表示该方法是需要进行 benchmark 的对象
:@Setup , 方法注解,会在执行 benchmark 之前被执行,正如其名,主要用于初始化
:@TearDown , 方法注解,与@Setup 相对的,会在所有 benchmark 执行结束以后执行,主要用于资源的回收等
:@Param , 成员注解,可以用来指定某项参数的多种情况
# 适合用来测试一个函数在不同的参数输入的情况下的性能
一、JMH 环境搭建
<properties>
<jmh.version>1.14.1</jmh.version>
</properties>
<!-- 压测工具 -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
<!-- 插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<finalName>microbenchmarks</finalName>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.openjdk.jmh.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
// 简单的测试案例
// 测试方法平均执行时间
@BenchmarkMode(Mode.AverageTime)
// 输出结果的时间粒度为微秒
@OutputTimeUnit(TimeUnit.MICROSECONDS)
// 每个测试线程一个实例
@State(Scope.Thread)
public class FirstBenchMark {
private static Logger log = LoggerFactory.getLogger(FirstBenchMark.class);
@Benchmark
public String stringConcat() {
String a = "a";
String b = "b";
String c = "c";
String s = a + b + c;
log.debug(s);
return s;
}
public static void main(String[] args) throws RunnerException {
// 使用一个单独进程执行测试,执行5遍warmup,然后执行5遍测试
Options opt = new OptionsBuilder().include(FirstBenchMark.class.getSimpleName()).forks(1).warmupIterations(5)
.measurementIterations(5).build();
new Runner(opt).run();
}
}