Skip to content

JMH, Java Microbenchmark Harness

OpenJDK 团队开发的一个用于 Java 微基准测试工具套件,主要是基于方法层面的基准测试,精度可达到纳秒级。它提供了一种标准、可靠且可重复的方式来衡量 Java 代码的性能,包括方法调用、对象创建以及其他类型的 JVM 级别的操作。JMH 通过生成优化过的字节码来确保基准测试不受常见陷阱的影响,如热身不足、垃圾回收干扰、编译器优化等,从而产生更准确的性能指标。

@State

  • Scope.Thread 每个线程分配一个实例(默认)
  • Scope.Benchmark 所有线程共享一个实例
  • Scope.Group 每个线程组共享一个实例

@BenchmarkMode

  • Mode.Throughput 每秒执行次数 opt/s
  • Mode.AverageTime 平均执行时间 s/op
  • Mode.SampleTime 抽样平均执行时间 s/op
  • Mode.SimpleShotTime 单次平均执行时间 s/op
  • Mode.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();
    }  
}