JMH(Java Microbenchmark Harness)是一个专门用于编写、运行和分析Java微基准测试的工具。它由OpenJDK团队开发,旨在提供精确的基准测试结果,避免常见的基准测试陷阱,如JVM的优化、即时编译(JIT)等影响。
基准测试的目的
JMH的特点
关键注解 ️
@Benchmark
:标记一个方法为基准测试方法。@State
:标记一个类为状态类,用于存储测试中的状态。@Setup
和 @TearDown
:分别在基准测试开始前和结束后执行的方法。@Warmup
:配置预热迭代次数和时间。@Measurement
:配置实际测试的迭代次数和时间。@Fork
:配置JVM实例的fork次数。测试模式
JAVA
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
@State(Scope.Thread) // 每个线程都有自己的状态实例
@BenchmarkMode(Mode.AverageTime) // 测试模式为平均时间
@OutputTimeUnit(TimeUnit.NANOSECONDS) // 输出时间单位为纳秒
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) // 预热3次,每次1秒
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) // 测试5次,每次1秒
@Fork(1) // 使用1个JVM实例
public class StringConcatBenchmark {
private String str1;
private String str2;
@Setup // 在基准测试开始前执行
public void setup() {
str1 = "Hello";
str2 = "World";
}
@Benchmark // 标记为基准测试方法
public String concatWithPlus() {
return str1 + " " + str2; // 使用 + 进行字符串拼接
}
@Benchmark // 另一个基准测试方法
public String concatWithStringBuilder() {
return new StringBuilder().append(str1).append(" ").append(str2).toString(); // 使用 StringBuilder
}
@TearDown // 在基准测试结束后执行
public void tearDown() {
// 这里可以清理资源
}
}
添加依赖
如果你使用 Maven,需要在 pom.xml
中添加 JMH 依赖:
org.openjdk.jmh
jmh-core
1.36
org.openjdk.jmh
jmh-generator-annprocess
1.36
provided
编译项目 ⚙️
运行以下命令编译项目并生成 JMH 基准测试的 JAR 文件:
mvn clean install
运行基准测试
运行生成的 JAR 文件来执行基准测试:
java -jar target/benchmarks.jar
运行后,你会看到类似以下的输出:
Benchmark Mode Cnt Score Error Units
StringConcatBenchmark.concatWithPlus avgt 5 23.456 ± 1.234 ns/op
StringConcatBenchmark.concatWithStringBuilder avgt 5 15.678 ± 0.987 ns/op
@State(Scope.Thread)
表示每个线程都会有一个独立的实例,避免线程竞争问题。
@BenchmarkMode(Mode.AverageTime)
测试模式为平均时间,还可以选择 Throughput
(吞吐量)、SampleTime
(采样时间)等。
@Warmup
和 @Measurement
@Warmup
:预热阶段,让 JVM 充分优化代码。@Measurement
:实际测试阶段。@Fork
指定运行的 JVM 实例数量,避免 JVM 优化对测试结果的影响。
@Benchmark
标记基准测试方法,方法内的代码会被 JMH 测量。
JMH是一个非常强大的工具,能够帮助开发者精确测量Java代码的性能。通过合理的配置和使用,可以避免常见的基准测试陷阱,得到可靠的测试结果。️