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的大多数常见用法,你可以根据需要调整参数和测试场景。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。