Java JMH常见的基准测试场景代码实例解读
作者:学亮编程手记
本文介绍Java JMH性能测试框架,涵盖方法比较、参数影响、吞吐量及多线程测试场景,通过@Benchmark、@State、@Param等注解定义基准,结合预热和优化处理确保测试结果准确可靠
Java JMH 代码示例
下面是一个完整的 JMH 示例,覆盖了常见的基准测试场景,包括方法性能比较、不同参数的影响、吞吐量测试、多线程测试等。
1. 基础设置
首先确保添加 JMH 依赖(Maven 配置):
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.37</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.37</version>
<scope>provided</scope>
</dependency>
2. 完整示例代码
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import java.util.concurrent.TimeUnit;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@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个进程
@State(Scope.Benchmark) // 状态共享范围
public class JMHExampleBenchmark {
// 测试参数:可以用@Param注解测试不同参数值的影响
@Param({"10", "100", "1000"})
public int size;
private List<Integer> arrayList;
private List<Integer> linkedList;
private int[] array;
private AtomicInteger atomicCounter;
private ConcurrentHashMap<Integer, Integer> concurrentMap;
private Random random;
// 初始化方法,在每个测试前执行
@Setup
public void setup() {
arrayList = new ArrayList<>(size);
linkedList = new LinkedList<>();
array = new int[size];
atomicCounter = new AtomicInteger(0);
concurrentMap = new ConcurrentHashMap<>();
random = new Random();
for (int i = 0; i < size; i++) {
arrayList.add(i);
linkedList.add(i);
array[i] = i;
concurrentMap.put(i, i);
}
}
// 场景1:ArrayList vs LinkedList 随机访问性能对比
@Benchmark
public void arrayListRandomAccess(Blackhole bh) {
for (int i = 0; i < size; i++) {
bh.consume(arrayList.get(random.nextInt(size)));
}
}
@Benchmark
public void linkedListRandomAccess(Blackhole bh) {
for (int i = 0; i < size; i++) {
bh.consume(linkedList.get(random.nextInt(size)));
}
}
// 场景2:数组遍历的不同方式性能对比
@Benchmark
public int arrayTraversalForLoop() {
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
@Benchmark
public int arrayTraversalForEach() {
int sum = 0;
for (int value : array) {
sum += value;
}
return sum;
}
// 场景3:字符串拼接方式性能对比
@Benchmark
public String stringConcatPlus() {
String result = "";
for (int i = 0; i < size; i++) {
result += i;
}
return result;
}
@Benchmark
public String stringConcatBuilder() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++) {
sb.append(i);
}
return sb.toString();
}
// 场景4:原子操作性能测试
@Benchmark
public void atomicIncrement() {
atomicCounter.incrementAndGet();
}
// 场景5:并发Map操作测试
@Benchmark
public void concurrentMapPut() {
int key = random.nextInt(size);
concurrentMap.put(key, key);
}
@Benchmark
public void concurrentMapGet(Blackhole bh) {
int key = random.nextInt(size);
bh.consume(concurrentMap.get(key));
}
// 场景6:多线程测试
@Benchmark
@Threads(4) // 使用4个线程
public void multiThreadedIncrement() {
atomicCounter.incrementAndGet();
}
// 场景7:吞吐量测试模式
@Benchmark
@BenchmarkMode(Mode.Throughput) // 测试吞吐量(ops/time)
@OutputTimeUnit(TimeUnit.SECONDS)
public void throughputTest() {
// 简单的数学运算
double result = Math.sin(random.nextDouble()) * Math.cos(random.nextDouble());
Blackhole.consumeCPU(100); // 模拟一些CPU工作
}
// 场景8:测试模式组合
@Benchmark
@BenchmarkMode({Mode.AverageTime, Mode.Throughput})
public void multiModeTest() {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
Blackhole.consumeCPU(50);
}
}
3. 运行JMH测试
创建一个主类来运行基准测试:
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
public class JMHRunner {
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHExampleBenchmark.class.getSimpleName())
.forks(1) // 使用1个进程
.build();
new Runner(opt).run();
}
}
4. 关键点说明
- @State:定义测试状态,可以共享数据
- @Setup:测试前的初始化方法
- @Benchmark:标记基准测试方法
- Blackhole:防止JVM优化掉无实际效果的操作
- @Param:参数化测试,可以测试不同输入规模的影响
- @Threads:多线程测试
- @BenchmarkMode:可以组合多种测试模式
- @Warmup:JVM预热设置,避免冷启动影响结果
5. 典型输出结果
运行后会输出类似这样的结果:
Benchmark (size) Mode Cnt Score Error Units JMHExample.arrayListRandomAccess 100 avgt 5 1234.567 ± 12.345 ns/op JMHExample.linkedListRandomAccess 100 avgt 5 56789.012 ± 678.901 ns/op JMHExample.stringConcatPlus 100 avgt 5 12345.678 ± 123.456 ns/op JMHExample.stringConcatBuilder 100 avgt 5 123.456 ± 1.234 ns/op JMHExample.throughputTest N/A thrpt 5 123456.789 ± 1234.567 ops/s
这个示例涵盖了JMH的大多数常见用法,你可以根据需要调整参数和测试场景。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
