JMH(Java Microbenchmark Harness)是一个专门用于编写、运行和分析Java微基准测试的工具。它由OpenJDK团队开发,旨在提供精确的基准测试结果,避免常见的基准测试陷阱,如JVM的优化、即时编译(JIT)等影响。
核心知识点 🧠
-
基准测试的目的 🎯
- 基准测试用于测量代码的性能,通常是在微秒或纳秒级别。
- 它可以帮助开发者识别性能瓶颈,优化代码。
-
JMH的特点 🌟
- 精确性:JMH通过多次迭代和预热来减少JVM优化对测试结果的影响。
- 可配置性:可以配置测试的模式(如吞吐量、平均时间、采样时间等)。
- 易于使用:通过注解和简单的API,开发者可以轻松编写基准测试。
-
关键注解 🏷️
@Benchmark:标记一个方法为基准测试方法。@State:标记一个类为状态类,用于存储测试中的状态。@Setup和@TearDown:分别在基准测试开始前和结束后执行的方法。@Warmup:配置预热迭代次数和时间。@Measurement:配置实际测试的迭代次数和时间。@Fork:配置JVM实例的fork次数。
-
测试模式 🧪
- Throughput(吞吐量) :测量单位时间内执行的操作次数。
- AverageTime(平均时间) :测量每次操作的平均时间。
- SampleTime(采样时间) :测量每次操作的时间分布。
- SingleShotTime(单次时间) :测量单次操作的时间。
示例代码 🧑💻 DEMO
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() {
// 这里可以清理资源
}
}
运行JMH测试 🏃♂️
-
添加依赖 📦
如果你使用 Maven,需要在pom.xml中添加 JMH 依赖:XML <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.36</version> </dependency> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.36</version> <scope>provided</scope> </dependency> -
编译项目 ⚙️
运行以下命令编译项目并生成 JMH 基准测试的 JAR 文件:BASH mvn clean install -
运行基准测试 🚀
运行生成的 JAR 文件来执行基准测试:BASH java -jar target/benchmarks.jar
输出结果示例 📊
运行后,你会看到类似以下的输出:
TEXT
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
- Score:每次操作的平均时间(单位:纳秒)。
- Error:误差范围。
- Units:时间单位。
代码解析 🧐
-
@State(Scope.Thread)
表示每个线程都会有一个独立的实例,避免线程竞争问题。 -
@BenchmarkMode(Mode.AverageTime)
测试模式为平均时间,还可以选择Throughput(吞吐量)、SampleTime(采样时间)等。 -
@Warmup和@Measurement@Warmup:预热阶段,让 JVM 充分优化代码。@Measurement:实际测试阶段。
-
@Fork
指定运行的 JVM 实例数量,避免 JVM 优化对测试结果的影响。 -
@Benchmark
标记基准测试方法,方法内的代码会被 JMH 测量。
总结 📚
JMH是一个非常强大的工具,能够帮助开发者精确测量Java代码的性能。通过合理的配置和使用,可以避免常见的基准测试陷阱,得到可靠的测试结果。🛠️