JMH, Java Microbenchmark Harness
OpenJDK 团队开发的一个用于 Java 微基准测试工具套件,主要是基于方法层面的基准测试,精度可达到纳秒级。它提供了一种标准、可靠且可重复的方式来衡量 Java 代码的性能,包括方法调用、对象创建以及其他类型的 JVM 级别的操作。JMH 通过生成优化过的字节码来确保基准测试不受常见陷阱的影响,如热身不足、垃圾回收干扰、编译器优化等,从而产生更准确的性能指标。
@State
Scope.Thread
每个线程分配一个实例(默认)Scope.Benchmark
所有线程共享一个实例Scope.Group
每个线程组共享一个实例
@BenchmarkMode
Mode.Throughput
每秒执行次数 opt/sMode.AverageTime
平均执行时间 s/opMode.SampleTime
抽样平均执行时间 s/opMode.SimpleShotTime
单次平均执行时间 s/opMode.All
所有模式执行一遍
@Measurement
压测次数
- iterations
- time
- timeUnit
- batchSize
@Warmup
预热
- iterations
- time
- timeUnit
- batchSize
@Fork
多分支进程执行测试
@Threads
线程数
@OutputTimeUnit
输出时间单位
@Param
不同参数情况
@Setup
基准测试前类初始化动作
@TearDown
基准测试后执行动作
JMH 陷阱
死码消除,代码不可达或不被使用会被简化。
java
@Benchmark
public void method() {
int a = 1;
int b = 1;
int c = a + b;
}
// 等价于
@Beachmark
public void method() {}
// 解除死码
// 1 return
@Benchmark
public int method() {
int a = 1;
int b = 1;
int c = a + b;
return c;
}
// 2 jmh Blackhole
@Benchmark
public void method(Blackhold blackhold) {
int a = 1;
int b = 1;
int c = a + b;
blackhold.consume(c);
}
Maven dependencies
xml
<properties>
<jmh-version>1.37</jmh-version>
</properties>
<dependencies>
<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>
</dependencies>
example
java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@State(Scope.Benchmark)
public class BenchmarkTest {
String s = "{\"color\": \"Blank\", \"type\": \"BMW\"}";
@State(Scope.Benchmark)
public static class BenchmarkState {
ObjectMapper GLOBAL_MAP = new ObjectMapper();
ThreadLocal<ObjectMapper> GLOBAL_MAP_THREAD = new ThreadLocal<>();
}
@Benchmark
public Map globalTest(BenchmarkState state) throws Exception {
Map map = state.GLOBAL_MAP.readValue(s, Map.class);
return map;
}
@Benchmark
public Map globalTestThreadLocal(BenchmarkState state) throws JsonProcessingException {
if (null == state.GLOBAL_MAP_THREAD.get()) state.GLOBAL_MAP_THREAD.set(new ObjectMapper());
Map map = state.GLOBAL_MAP_THREAD.get().readValue(s, Map.class);
return map;
}
@Benchmark
public Map localTest() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Map map = objectMapper.readValue(s, Map.class);
return map;
}
public static void main(String[] args) throws RunnerException {
new Runner(new OptionsBuilder()
.include(BenchmarkTest.class.getSimpleName())
.mode(Mode.SingleShotTime)
.timeUnit(TimeUnit.SECONDS)
.forks(1)
.threads(Runtime.getRuntime().availableProcessors())
.warmupIterations(1)
.measurementIterations(1)
.build()).run();
}
}