Java JMH常見的基準(zhǔn)測試場景代碼實例解讀
Java JMH 代碼示例
下面是一個完整的 JMH 示例,覆蓋了常見的基準(zhǔn)測試場景,包括方法性能比較、不同參數(shù)的影響、吞吐量測試、多線程測試等。
1. 基礎(chǔ)設(shè)置
首先確保添加 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) // 測試模式:平均執(zhí)行時間
@OutputTimeUnit(TimeUnit.NANOSECONDS) // 輸出時間單位
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS) // 預(yù)熱:3輪,每輪1秒
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) // 測試:5輪,每輪1秒
@Fork(1) // 使用1個進(jìn)程
@State(Scope.Benchmark) // 狀態(tài)共享范圍
public class JMHExampleBenchmark {
// 測試參數(shù):可以用@Param注解測試不同參數(shù)值的影響
@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;
// 初始化方法,在每個測試前執(zhí)行
@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 隨機(jī)訪問性能對比
@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:數(shù)組遍歷的不同方式性能對比
@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:并發(fā)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() {
// 簡單的數(shù)學(xué)運(yùn)算
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. 運(yùn)行JMH測試
創(chuàng)建一個主類來運(yùn)行基準(zhǔn)測試:
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個進(jìn)程
.build();
new Runner(opt).run();
}
}
4. 關(guān)鍵點說明
- @State:定義測試狀態(tài),可以共享數(shù)據(jù)
- @Setup:測試前的初始化方法
- @Benchmark:標(biāo)記基準(zhǔn)測試方法
- Blackhole:防止JVM優(yōu)化掉無實際效果的操作
- @Param:參數(shù)化測試,可以測試不同輸入規(guī)模的影響
- @Threads:多線程測試
- @BenchmarkMode:可以組合多種測試模式
- @Warmup:JVM預(yù)熱設(shè)置,避免冷啟動影響結(jié)果
5. 典型輸出結(jié)果
運(yùn)行后會輸出類似這樣的結(jié)果:
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的大多數(shù)常見用法,你可以根據(jù)需要調(diào)整參數(shù)和測試場景。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決創(chuàng)建springboot后啟動報錯:Failed?to?bind?properties?under‘spri
在Spring?Boot項目中,application.properties和application.yml是用于配置參數(shù)的兩種文件格式,properties格式簡潔但不支持層次結(jié)構(gòu),而yml格式支持層次性,可讀性更好,在yml文件中,要注意細(xì)節(jié),比如冒號后面需要空格2024-10-10
springboot(thymeleaf)中th:field和th:value的區(qū)別及說明
這篇文章主要介紹了springboot(thymeleaf)中th:field和th:value的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
Spring Boot Admin管理監(jiān)控數(shù)據(jù)的方法
本篇文章主要介紹了Spring Boot Admin管理監(jiān)控數(shù)據(jù)的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
IDEA?Debug過程中使用Drop?Frame或Reset?Frame實現(xiàn)操作回退的方法
在IDEA中就提供了一個幫助你回退代碼的機(jī)會,但這個方法并不是萬能的,好了,下面就來具體說說IDEA?Debug過程中使用Drop?Frame或Reset?Frame實現(xiàn)操作回退的方法,感興趣的朋友一起看看吧2022-04-04
Spring?Boot最經(jīng)典的20道面試題你都會了嗎
Spring Boot是現(xiàn)代化的Java應(yīng)用程序開發(fā)框架,具有高度的靈活性和可擴(kuò)展性,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot最經(jīng)典的20道面試題,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-06-06

