別再寫(xiě) main 方法測(cè)試了,太 Low!這才是專業(yè) Java 測(cè)試方法!
"If you cannot measure it, you cannot improve it".

JMH的使用場(chǎng)景:
定量分析某個(gè)熱點(diǎn)函數(shù)的優(yōu)化效果 想定量地知道某個(gè)函數(shù)需要執(zhí)行多長(zhǎng)時(shí)間,以及執(zhí)行時(shí)間和輸入變量的相關(guān)性 對(duì)比一個(gè)函數(shù)的多種實(shí)現(xiàn)方式
1. 測(cè)試項(xiàng)目構(gòu)建
第一種是使用命令行構(gòu)建,在指定目錄下執(zhí)行以下命令:
$ mvn archetype:generate \-DinteractiveMode=false \-DarchetypeGroupId=org.openjdk.jmh \-DarchetypeArtifactId=jmh-java-benchmark-archetype \-DgroupId=org.sample \-DartifactId=test \-Dversion=1.0

org.openjdk.jmh jmh-core ${jmh.version} org.openjdk.jmh jmh-generator-annprocess ${jmh.version} provided
2. 編寫(xiě)性能測(cè)試
/*** @author Richard_yyf* @version 1.0 2019/8/27*/@State(Scope.Benchmark)@OutputTimeUnit(TimeUnit.SECONDS)@Threads(Threads.MAX)public class LinkedListIterationBenchMark {private static final int SIZE = 10000;private Listlist = new LinkedList<>(); @Setuppublic void setUp() {for (int i = 0; i < SIZE; i++) {list.add(String.valueOf(i));}}@Benchmark@BenchmarkMode(Mode.Throughput)public void forIndexIterate() {for (int i = 0; i < list.size(); i++) {list.get(i);System.out.print("");}}@Benchmark@BenchmarkMode(Mode.Throughput)public void forEachIterate() {for (String s : list) {System.out.print("");}}}
3. 執(zhí)行測(cè)試
生成jar文件的形式主要是針對(duì)一些比較大的測(cè)試,可能對(duì)機(jī)器性能或者真實(shí)環(huán)境模擬有一些需求,需要將測(cè)試方法寫(xiě)好了放在linux環(huán)境執(zhí)行。具體命令如下
$ mvn clean install$ java -jar target/benchmarks.jar
我們?nèi)粘V杏龅降囊话闶且恍┬y(cè)試,比如我上面寫(xiě)的例子,直接在IDE中跑就好了。啟動(dòng)方式如下:
public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder().include(LinkedListIterationBenchMark.class.getSimpleName()).forks(1).warmupIterations(2).measurementIterations(2).output("E:/Benchmark.log").build();new Runner(opt).run();}
輸出結(jié)果如下
最后的結(jié)果:
Benchmark Mode Cnt Score Error UnitsLinkedListIterationBenchMark.forEachIterate thrpt 2 1192.380 ops/sLinkedListIterationBenchMark.forIndexIterate thrpt 2 206.866 ops/s
# Detecting actual CPU count: 12 detected# JMH version: 1.21# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11# VM invoker: C:\Program Files\Java\jdk1.8.0_131\jre\bin\java.exe# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=65175:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\bin -Dfile.encoding=UTF-8# Warmup: 2 iterations, 10 s each# Measurement: 2 iterations, 10 s each# Timeout: 10 min per iteration# Threads: 12 threads, will synchronize iterations# Benchmark mode: Throughput, ops/time# Benchmark: org.sample.jmh.LinkedListIterationBenchMark.forEachIterate# Run progress: 0.00% complete, ETA 00:01:20# Fork: 1 of 1# Warmup Iteration 1: 1189.267 ops/s# Warmup Iteration 2: 1197.321 ops/sIteration 1: 1193.062 ops/sIteration 2: 1191.698 ops/sResult "org.sample.jmh.LinkedListIterationBenchMark.forEachIterate":1192.380 ops/s# JMH version: 1.21# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11# VM invoker: C:\Program Files\Java\jdk1.8.0_131\jre\bin\java.exe# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=65175:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\bin -Dfile.encoding=UTF-8# Warmup: 2 iterations, 10 s each# Measurement: 2 iterations, 10 s each# Timeout: 10 min per iteration# Threads: 12 threads, will synchronize iterations# Benchmark mode: Throughput, ops/time# Benchmark: org.sample.jmh.LinkedListIterationBenchMark.forIndexIterate# Run progress: 50.00% complete, ETA 00:00:40# Fork: 1 of 1# Warmup Iteration 1: 205.676 ops/s# Warmup Iteration 2: 206.512 ops/sIteration 1: 206.542 ops/sIteration 2: 207.189 ops/sResult "org.sample.jmh.LinkedListIterationBenchMark.forIndexIterate":206.866 ops/s# Run complete. Total time: 00:01:21REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up onwhy the numbers are the way they are. Use profilers (see -prof, -lprof), design factorialexperiments, perform baseline and negative tests that provide experimental control, make surethe benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.Do not assume the numbers tell you what you want them to tell.Benchmark Mode Cnt Score Error UnitsLinkedListIterationBenchMark.forEachIterate thrpt 2 1192.380 ops/sLinkedListIterationBenchMark.forIndexIterate??thrpt????2???206.866??????????ops/s
下面我們來(lái)詳細(xì)介紹一下相關(guān)的注解
@BenchmarkMode
微基準(zhǔn)測(cè)試類型。JMH 提供了以下幾種類型進(jìn)行支持:

可以注釋在方法級(jí)別,也可以注釋在類級(jí)別,
@BenchmarkMode(Mode.All)public class LinkedListIterationBenchMark {...}@Benchmark@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})public void m() {...}
@Warmup
這個(gè)單詞的意思就是預(yù)熱,iterations = 3就是指預(yù)熱輪數(shù)。
@Benchmark@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})@Warmup(iterations = 3)public void m() {...}
正式度量計(jì)算的輪數(shù)。
@Benchmark@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})@Measurement(iterations = 3)public void m() {...}
每個(gè)進(jìn)程中的測(cè)試線程。
@Threads(Threads.MAX)public class LinkedListIterationBenchMark {...}
@Fork
進(jìn)行 fork 的次數(shù)。如果 fork 數(shù)是3的話,則 JMH 會(huì) fork 出3個(gè)進(jìn)程來(lái)進(jìn)行測(cè)試。
@Benchmark@BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})@Fork(value = 3)public void m() {...}
@OutputTimeUnit
基準(zhǔn)測(cè)試結(jié)果的時(shí)間類型。一般選擇秒、毫秒、微秒。
@OutputTimeUnit(TimeUnit.SECONDS)public class LinkedListIterationBenchMark {...}
@Benchmark
@Param
@Setup
方法級(jí)注解,這個(gè)注解的作用就是我們需要在測(cè)試之前進(jìn)行一些準(zhǔn)備工作,比如對(duì)一些數(shù)據(jù)的初始化之類的。微信搜索公眾號(hào):互聯(lián)網(wǎng)架構(gòu)師,回復(fù)“2T”,獲取更多精品學(xué)習(xí)資料
@TearDown
@State
啟動(dòng)方法
/*** 僅限于IDE中運(yùn)行* 命令行模式 則是 build 然后 java -jar 啟動(dòng)** 1. 這是benchmark 啟動(dòng)的入口* 2. 這里同時(shí)還完成了JMH測(cè)試的一些配置工作* 3. 默認(rèn)場(chǎng)景下,JMH會(huì)去找尋標(biāo)注了@Benchmark的方法,可以通過(guò)include和exclude兩個(gè)方法來(lái)完成包含以及排除的語(yǔ)義*/public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder()// 包含語(yǔ)義// 可以用方法名,也可以用XXX.class.getSimpleName().include("Helloworld")// 排除語(yǔ)義.exclude("Pref")// 預(yù)熱10輪.warmupIterations(10)// 代表正式計(jì)量測(cè)試做10輪,// 而每次都是先執(zhí)行完預(yù)熱再執(zhí)行正式計(jì)量,// 內(nèi)容都是調(diào)用標(biāo)注了@Benchmark的代碼。.measurementIterations(10)// forks(3)指的是做3輪測(cè)試,// 因?yàn)橐淮螠y(cè)試無(wú)法有效的代表結(jié)果,// 所以通過(guò)3輪測(cè)試較為全面的測(cè)試,// 而每一輪都是先預(yù)熱,再正式計(jì)量。.forks(3).output("E:/Benchmark.log").build();new Runner(opt).run();}
相關(guān)閱讀:2T架構(gòu)師學(xué)習(xí)資料干貨分享
PS:如果覺(jué)得我的分享不錯(cuò),歡迎大家隨手點(diǎn)贊、轉(zhuǎn)發(fā)、在看。
