不要再用main方法测试代码性能了,用这款JDK自带工具
前言
作為軟件開(kāi)發(fā)人員,我們通常會(huì)寫(xiě)一些測(cè)試程序用來(lái)對(duì)比不同算法、不同工具的性能問(wèn)題。而最常見(jiàn)的做法是寫(xiě)一個(gè)main方法,構(gòu)造模擬場(chǎng)景進(jìn)行并發(fā)測(cè)試。
如果細(xì)心的朋友可能已經(jīng)發(fā)現(xiàn),每次測(cè)試結(jié)果誤差很大,有時(shí)候測(cè)試出的結(jié)果甚至與事實(shí)相反。當(dāng)然,這不排除是因?yàn)檐浻布h(huán)境因素導(dǎo)致,但更多的可能是因?yàn)樗褂脺y(cè)試方法自身有問(wèn)題。
比如,不同需要性能比較方法放到一個(gè)虛擬機(jī)里調(diào)用,有可能會(huì)互相影響,缺少預(yù)熱的過(guò)程等。
本文給大家推薦一款JDK9及以后自帶的一款可用于軟件基準(zhǔn)測(cè)試的工具JMH(Java Microbenchmark Harness)。
JMH簡(jiǎn)介
JMH是用于代碼微基準(zhǔn)測(cè)試的工具套件,主要是基于方法層面的基準(zhǔn)測(cè)試,精度可以達(dá)到納秒級(jí)。
何謂Micro Benchmark呢?簡(jiǎn)單的來(lái)說(shuō)就是基于方法層面的基準(zhǔn)測(cè)試,精度可以達(dá)到微秒級(jí)。當(dāng)你定位到熱點(diǎn)方法,希望進(jìn)一步優(yōu)化方法性能的時(shí)候,就可以使用JMH對(duì)優(yōu)化的結(jié)果進(jìn)行量化的分析。
這款工具是由Oracle內(nèi)部實(shí)現(xiàn)JIT的作者所寫(xiě)。我們知道JIT(Java即時(shí)編譯器)是將JVM優(yōu)化的所有高效手段和技術(shù)都使用上的地方??上攵?#xff0c;開(kāi)發(fā)者比任何人都更加了解JVM和JIT對(duì)基準(zhǔn)測(cè)試的影響。
因此,這款工具是值得我們信賴(lài)和在實(shí)踐中進(jìn)行使用的。而且使用起來(lái)也非常方便。
使用場(chǎng)景
JMH不僅能幫我們測(cè)試一些常見(jiàn)類(lèi)的性能,比如對(duì)比StringBuffer和StringBuilder的性能、對(duì)比不同算法的在不同數(shù)據(jù)量的性能等,還能夠幫助我們對(duì)系統(tǒng)中發(fā)現(xiàn)的熱點(diǎn)代碼進(jìn)行量化分析。
JMH通常用于以下應(yīng)用場(chǎng)景:
測(cè)試某個(gè)方法在穩(wěn)定執(zhí)行的情況下所需時(shí)間,以及執(zhí)行時(shí)間和問(wèn)題規(guī)模的相關(guān)性;
對(duì)比接口不同實(shí)現(xiàn)在給定條件下的吞吐量
查看多少百分比的請(qǐng)求在多長(zhǎng)時(shí)間內(nèi)完成
使用實(shí)例
依賴(lài)引入
如果你使用的是JDK9或以上版本,則JDK中已經(jīng)自帶了該工具,直接使用即可。如果你使用的是其他版本則可以通過(guò)maven直接引入以下依賴(lài):
<dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-core</artifactId><version>1.27</version> </dependency> <dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-generator-annprocess</artifactId><version>1.27</version> </dependency>其中1.27是當(dāng)前的最新版本,可根據(jù)實(shí)際需要更新或降低版本。
測(cè)試案例
下面以StringBuffer和StringBuilder的性能測(cè)試對(duì)比為例來(lái)進(jìn)行基準(zhǔn)測(cè)試。
//使用模式?默認(rèn)是Mode.Throughput @BenchmarkMode(Mode.AverageTime) //?配置預(yù)熱次數(shù),默認(rèn)是每次運(yùn)行1秒,運(yùn)行10次,這里設(shè)置為3次 @Warmup(iterations?=?3,?time?=?1) //?本例是一次運(yùn)行4秒,總共運(yùn)行3次,在性能對(duì)比時(shí)候,采用默認(rèn)1秒即可 @Measurement(iterations?=?3,?time?=?4) //?配置同時(shí)起多少個(gè)線程執(zhí)行 @Threads(1) //代表啟動(dòng)多個(gè)單獨(dú)的進(jìn)程分別測(cè)試每個(gè)方法,這里指定為每個(gè)方法啟動(dòng)一個(gè)進(jìn)程 @Fork(1) //?定義類(lèi)實(shí)例的生命周期,Scope.Benchmark:所有測(cè)試線程共享一個(gè)實(shí)例,用于測(cè)試有狀態(tài)實(shí)例在多線程共享下的性能 @State(value?=?Scope.Benchmark) //?統(tǒng)計(jì)結(jié)果的時(shí)間單元 @OutputTimeUnit(TimeUnit.NANOSECONDS) public?class?JmhTest?{@Param(value?=?{"10",?"50",?"100"})private?int?length;public?static?void?main(String[]?args)?throws?RunnerException?{Options?opt?=?new?OptionsBuilder().include(JmhTest.class.getSimpleName()).result("result.json").resultFormat(ResultFormatType.JSON).build();new?Runner(opt).run();}@Benchmarkpublic?void?testStringBufferAdd(Blackhole?blackhole)?{StringBuffer?sb?=?new?StringBuffer();for?(int?i?=?0;?i?<?length;?i++)?{sb.append(i);}blackhole.consume(sb.toString());}@Benchmarkpublic?void?testStringBuilderAdd(Blackhole?blackhole)?{StringBuilder?sb?=?new?StringBuilder();for?(int?i?=?0;?i?<?length;?i++)?{sb.append(i);}blackhole.consume(sb.toString());} }上面介紹概念時(shí)已經(jīng)提到Benchmark為基準(zhǔn)測(cè)試,在使用中只需對(duì)要測(cè)試的方法添加@Benchmark注解即可。而在測(cè)試類(lèi)JmhTest指定測(cè)試的預(yù)熱、線程、測(cè)試維度等信息。
main方法中通過(guò)OptionsBuilder構(gòu)造測(cè)試配置對(duì)象Options,并傳入Runner,啟動(dòng)測(cè)試。這里指定測(cè)試結(jié)果為json格式,同時(shí)會(huì)將結(jié)果存儲(chǔ)在result.json文件當(dāng)中。
執(zhí)行測(cè)試
執(zhí)行main方法,控制臺(tái)首先會(huì)打印出如下信息:
#?JMH?version:?1.27 #?VM?version:?JDK?1.8.0_271,?Java?HotSpot(TM)?64-Bit?Server?VM,?25.271-b09 #?VM?invoker:?/Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/jre/bin/java #?VM?options:?-javaagent:/Applications/IntelliJ?IDEA.app/Contents/lib/idea_rt.jar=56800:/Applications/IntelliJ?IDEA.app/Contents/bin?-Dfile.encoding=UTF-8 #?JMH?blackhole?mode:?full?blackhole?+?dont-inline?hint #?Warmup:?3?iterations,?1?s?each #?Measurement:?3?iterations,?4?s?each #?Timeout:?10?min?per?iteration #?Threads:?1?thread,?will?synchronize?iterations #?Benchmark?mode:?Average?time,?time/op #?Benchmark:?com.choupangxia.strings.JmhTest.testStringBufferAdd #?Parameters:?(length?=?10)這些信息主要用來(lái)展示測(cè)試的基本信息,包括jdk、JVM、預(yù)熱配置、執(zhí)行輪次、執(zhí)行時(shí)間、執(zhí)行線程、測(cè)試的統(tǒng)計(jì)單位等。
#?Warmup?Iteration???1:?76.124?ns/op #?Warmup?Iteration???2:?77.703?ns/op #?Warmup?Iteration???3:?249.515?ns/op這是對(duì)待測(cè)試方法的預(yù)熱處理,這部分不會(huì)記入測(cè)試結(jié)果。預(yù)熱主要讓JVM對(duì)被測(cè)代碼進(jìn)行足夠多的優(yōu)化,比如JIT編譯器的優(yōu)化。
Iteration???1:?921.191?ns/op Iteration???2:?897.729?ns/op Iteration???3:?890.245?ns/opResult?"com.choupangxia.strings.JmhTest.testStringBuilderAdd":903.055?±(99.9%)?294.557?ns/op?[Average](min,?avg,?max)?=?(890.245,?903.055,?921.191),?stdev?=?16.146CI?(99.9%):?[608.498,?1197.612]?(assumes?normal?distribution)顯示每次(共3次)迭代執(zhí)行速率,最后進(jìn)行統(tǒng)計(jì)。這里是對(duì)testStringBuilderAdd方法執(zhí)行l(wèi)ength為100的測(cè)試,通過(guò) (min, avg, max) 三項(xiàng)可以看出最小時(shí)間、平均時(shí)間、最大時(shí)間的值,單位為ns。stdev顯示的是誤差時(shí)間。
通常情況下,我們只用看最后的結(jié)果即可:
Benchmark?????????????????????(length)??Mode??Cnt?????Score??????Error??Units JmhTest.testStringBufferAdd???????????????10??avgt????3????92.599?±??105.019??ns/op JmhTest.testStringBufferAdd???????????????50??avgt????3???582.974?±??580.536??ns/op JmhTest.testStringBufferAdd??????????????100??avgt????3??1131.460?±?1109.380??ns/op JmhTest.testStringBuilderAdd????????10??avgt????3????76.072?±????2.824??ns/op JmhTest.testStringBuilderAdd????????50??avgt????3???450.325?±???14.271??ns/op JmhTest.testStringBuilderAdd???????100??avgt????3???903.055?±??294.557??ns/op看到上述結(jié)果我們可能會(huì)很吃驚,我們知道StringBuffer要比StringBuilder的性能低一些,但結(jié)果發(fā)現(xiàn)它們的之間的差別并不是很大。這是因?yàn)镴IT編譯器進(jìn)行了優(yōu)化,比如當(dāng)JVM發(fā)現(xiàn)在測(cè)試當(dāng)中StringBuffer并沒(méi)有發(fā)生逃逸,于是就進(jìn)行了鎖消除操作。
常用注解
下面對(duì)JHM當(dāng)中常用的注解進(jìn)行說(shuō)明,以便大家可以更精確的使用。
@BenchmarkMode
配置Mode選項(xiàng),作用于類(lèi)或者方法上,其value屬性為Mode數(shù)組,可同時(shí)支持多種Mode,如:@BenchmarkMode({Mode.SampleTime, Mode.AverageTime}),也可設(shè)為Mode.All,即全部執(zhí)行一遍。
org.openjdk.jmh.annotations.Mode為枚舉類(lèi),對(duì)應(yīng)的源代碼如下:
public?enum?Mode?{Throughput("thrpt",?"Throughput,?ops/time"),AverageTime("avgt",?"Average?time,?time/op"),SampleTime("sample",?"Sampling?time"),SingleShotTime("ss",?"Single?shot?invocation?time"),All("all",?"All?benchmark?modes");//?省略其他內(nèi)容 }不同模式之間,測(cè)量的維度或測(cè)量的方式不同。目前JMH共有四種模式:
Throughput:整體吞吐量,例如“1秒內(nèi)可以執(zhí)行多少次調(diào)用”,單位為ops/time;
AverageTime:調(diào)用的平均時(shí)間,例如“每次調(diào)用平均耗時(shí)xxx毫秒”,單位為time/op;
SampleTime:隨機(jī)取樣,最后輸出取樣結(jié)果的分布,,例如“99%的調(diào)用在xxx毫秒以?xún)?nèi),99.99%的調(diào)用在xxx毫秒以?xún)?nèi)”;
SingleShotTime:以上模式都是默認(rèn)一次iteration是1s,只有SingleShotTime是只運(yùn)行一次。往往同時(shí)把warmup次數(shù)設(shè)為0,用于測(cè)試?yán)鋯?dòng)時(shí)的性能;
All:上面的所有模式都執(zhí)行一次;
@Warmup
在執(zhí)行@Benchmark之前進(jìn)行預(yù)熱操作,確保測(cè)試的準(zhǔn)確性,可用于類(lèi)或者方法上。默認(rèn)是每次運(yùn)行1秒,運(yùn)行10次。
其中@Warmup有以下屬性:
iterations:預(yù)熱的次數(shù);Iteration是JMH進(jìn)行測(cè)試的最小單位,在大部分模式下,一次iteration代表的是一秒,JMH會(huì)在這一秒內(nèi)不斷調(diào)用需要benchmark的方法,然后根據(jù)模式對(duì)其采樣,計(jì)算吞吐量,計(jì)算平均執(zhí)行時(shí)間等。
time:每次預(yù)熱的時(shí)間;
timeUnit:時(shí)間的單位,默認(rèn)秒;
batchSize:批處理大小,每次操作調(diào)用幾次方法;
JIT在執(zhí)行的過(guò)程中會(huì)將熱點(diǎn)代碼編譯為機(jī)器碼,并進(jìn)行各種優(yōu)化,從而提高執(zhí)行效率。預(yù)熱的主要目的是讓JVM的JIT機(jī)制生效,讓結(jié)果更接近真實(shí)效果。
@State
類(lèi)注解,JMH測(cè)試類(lèi)必須使用@State注解,不然會(huì)提示無(wú)法運(yùn)行。
State定義了一個(gè)類(lèi)實(shí)例的生命周期(作用范圍),可以類(lèi)比Spring Bean的Scope。因?yàn)楹芏郻enchmark會(huì)需要一些表示狀態(tài)的類(lèi),JMH會(huì)根據(jù)scope來(lái)進(jìn)行實(shí)例化和共享操作。
@State可以被繼承使用,如果父類(lèi)定義了該注解,子類(lèi)則無(wú)需定義。
由于JMH允許多線程同時(shí)執(zhí)行測(cè)試,不同的選項(xiàng)含義如下:
Scope.Thread:默認(rèn)的State,該狀態(tài)為每個(gè)線程獨(dú)享,每個(gè)測(cè)試線程分配一個(gè)實(shí)例;
Scope.Benchmark:該狀態(tài)在所有線程間共享,所有測(cè)試線程共享一個(gè)實(shí)例,用于測(cè)試有狀態(tài)實(shí)例在多線程共享下的性能;
Scope.Group:該狀態(tài)為同一個(gè)組里面所有線程共享。
@OutputTimeUnit
benchmark統(tǒng)計(jì)結(jié)果所使用的時(shí)間單位,可用于類(lèi)或者方法注解,使用java.util.concurrent.TimeUnit中的標(biāo)準(zhǔn)時(shí)間單位。
@Measurement
度量,其實(shí)就是實(shí)際調(diào)用方法所需要配置的一些基本測(cè)試參數(shù),可用于類(lèi)或者方法上。配置屬性項(xiàng)目和作用與@Warmup相同。
一般比較重的程序可以進(jìn)行大量的測(cè)試,放到服務(wù)器上運(yùn)行。在性能對(duì)比時(shí),采用默認(rèn)1秒即可,如果用jvisualvm做性能監(jiān)控,可以指定一個(gè)較長(zhǎng)時(shí)間運(yùn)行。
@Threads
每個(gè)進(jìn)程中同時(shí)起多少個(gè)線程執(zhí)行,可用于類(lèi)或者方法上。默認(rèn)值是Runtime.getRuntime().availableProcessors(),根據(jù)具體情況選擇,一般為cpu乘以2。
@Fork
代表啟動(dòng)多個(gè)單獨(dú)的進(jìn)程分別測(cè)試每個(gè)方法,可用于類(lèi)或者方法上。如果fork數(shù)是2的話,則JMH會(huì)fork出兩個(gè)進(jìn)程來(lái)進(jìn)行測(cè)試。
JVM因?yàn)槭褂昧藀rofile-guided optimization而“臭名昭著”,這對(duì)于微基準(zhǔn)測(cè)試來(lái)說(shuō)十分不友好,因?yàn)椴煌瑴y(cè)試方法的profile混雜在一起,“互相傷害”彼此的測(cè)試結(jié)果。對(duì)于每個(gè)@Benchmark方法使用一個(gè)獨(dú)立的進(jìn)程可以解決這個(gè)問(wèn)題,這也是JMH的默認(rèn)選項(xiàng)。注意不要設(shè)置為0,設(shè)置為n則會(huì)啟動(dòng)n個(gè)進(jìn)程執(zhí)行測(cè)試(似乎也沒(méi)有太大意義)。fork選項(xiàng)也可以通過(guò)方法注解以及啟動(dòng)參數(shù)來(lái)設(shè)置。
@Param
屬性級(jí)注解,指定某項(xiàng)參數(shù)的多種情況,特別適合用來(lái)測(cè)試一個(gè)函數(shù)在不同的參數(shù)輸入的情況下的性能,只能作用在字段上,使用該注解必須定義@State注解。
@Param注解接收一個(gè)String數(shù)組,在@Setup方法執(zhí)行前轉(zhuǎn)化為對(duì)應(yīng)的數(shù)據(jù)類(lèi)型。多個(gè)@Param注解的成員之間是乘積關(guān)系,譬如有兩個(gè)用@Param注解的字段,第一個(gè)有5個(gè)值,第二個(gè)字段有2個(gè)值,那么每個(gè)測(cè)試方法會(huì)跑5*2=10次。
@Benchmark
方法注解,表示該方法是需要進(jìn)行benchmark的對(duì)象,用法和JUnit的@Test類(lèi)似。
@Setup
方法注解,這個(gè)注解的作用就是我們需要在測(cè)試之前進(jìn)行一些準(zhǔn)備工作,比如對(duì)一些數(shù)據(jù)的初始化之類(lèi)的。
@TearDown
方法注解,與@Setup相對(duì)的,會(huì)在所有benchmark執(zhí)行結(jié)束以后執(zhí)行,比如關(guān)閉線程池,數(shù)據(jù)庫(kù)連接等的,主要用于資源的回收等。
Threads
每個(gè)fork進(jìn)程使用多少個(gè)線程去執(zhí)行測(cè)試方法,默認(rèn)值是Runtime.getRuntime().availableProcessors()。
@Group
方法注解,可以把多個(gè)benchmark定義為同一個(gè)group,則它們會(huì)被同時(shí)執(zhí)行,譬如用來(lái)模擬生產(chǎn)者-消費(fèi)者讀寫(xiě)速度不一致情況下的表現(xiàn)。
@Level
用于控制@Setup,@TearDown的調(diào)用時(shí)機(jī),默認(rèn)是Level.Trial。
Trial:每個(gè)benchmark方法前后;
Iteration:每個(gè)benchmark方法每次迭代前后;
Invocation:每個(gè)benchmark方法每次調(diào)用前后,謹(jǐn)慎使用,需留意javadoc注釋;
JMH注意事項(xiàng)
無(wú)用代碼消除(Dead Code Elimination)
現(xiàn)代編譯器是十分聰明的,它們會(huì)對(duì)代碼進(jìn)行推導(dǎo)分析,判定哪些代碼是無(wú)用的然后進(jìn)行去除,這種行為對(duì)微基準(zhǔn)測(cè)試是致命的,它會(huì)使你無(wú)法準(zhǔn)確測(cè)試出你的方法性能。
JMH本身已經(jīng)對(duì)這種情況做了處理,要記住:1.永遠(yuǎn)不要寫(xiě)void方法;2.在方法結(jié)束返回計(jì)算結(jié)果。有時(shí)候如果需要返回多于一個(gè)結(jié)果,可以考慮自行合并計(jì)算結(jié)果,或者使用JMH提供的BlackHole對(duì)象:
/**?This?demonstrates?Option?A:**?Merge?multiple?results?into?one?and?return?it.*?This?is?OK?when?is?computation?is?relatively?heavyweight,?and?merging*?the?results?does?not?offset?the?results?much.*/ @Benchmark public?double?measureRight_1()?{return?Math.log(x1)?+?Math.log(x2); } /**?This?demonstrates?Option?B:**?Use?explicit?Blackhole?objects,?and?sink?the?values?there.*?(Background:?Blackhole?is?just?another?@State?object,?bundled?with?JMH).*/ @Benchmark public?void?measureRight_2(Blackhole?bh)?{bh.consume(Math.log(x1));bh.consume(Math.log(x2)); }再比如下面代碼:
@Benchmark public?void?testStringAdd(Blackhole?blackhole)?{String?a?=?"";for?(int?i?=?0;?i?<?length;?i++)?{a?+=?i;} }JVM可能會(huì)認(rèn)為變量a從來(lái)沒(méi)有使用過(guò),從而進(jìn)行優(yōu)化把整個(gè)方法內(nèi)部代碼移除掉,這就會(huì)影響測(cè)試結(jié)果。
JMH提供了兩種方式避免這種問(wèn)題,一種是將這個(gè)變量作為方法返回值return a,一種是通過(guò)Blackhole的consume來(lái)避免JIT 的優(yōu)化消除。
常量折疊(Constant Folding)
常量折疊是一種現(xiàn)代編譯器優(yōu)化策略,例如,i = 320 * 200 * 32,多數(shù)的現(xiàn)代編譯器不會(huì)真的產(chǎn)生兩個(gè)乘法的指令再將結(jié)果儲(chǔ)存下來(lái),取而代之的,它們會(huì)辨識(shí)出語(yǔ)句的結(jié)構(gòu),并在編譯時(shí)期將數(shù)值計(jì)算出來(lái)(i = 2,048,000)。
在微基準(zhǔn)測(cè)試中,如果你的計(jì)算輸入是可預(yù)測(cè)的,也不是一個(gè)@State實(shí)例變量,那么很可能會(huì)被JIT給優(yōu)化掉。對(duì)此,JMH的建議是:1.永遠(yuǎn)從@State實(shí)例中讀取你的方法輸入;2.返回你的計(jì)算結(jié)果;3.或者考慮使用BlackHole對(duì)象;
見(jiàn)如下官方例子:
@State(Scope.Thread) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public?class?JMHSample_10_ConstantFold?{private?double?x?=?Math.PI;private?final?double?wrongX?=?Math.PI;@Benchmarkpublic?double?baseline()?{//?simply?return?the?value,?this?is?a?baselinereturn?Math.PI;}@Benchmarkpublic?double?measureWrong_1()?{//?This?is?wrong:?the?source?is?predictable,?and?computation?is?foldable.return?Math.log(Math.PI);}@Benchmarkpublic?double?measureWrong_2()?{//?This?is?wrong:?the?source?is?predictable,?and?computation?is?foldable.return?Math.log(wrongX);}@Benchmarkpublic?double?measureRight()?{//?This?is?correct:?the?source?is?not?predictable.return?Math.log(x);}public?static?void?main(String[]?args)?throws?RunnerException?{Options?opt?=?new?OptionsBuilder().include(JMHSample_10_ConstantFold.class.getSimpleName()).warmupIterations(5).measurementIterations(5).forks(1).build();new?Runner(opt).run();} }循環(huán)展開(kāi)(Loop Unwinding)
循環(huán)展開(kāi)最常用來(lái)降低循環(huán)開(kāi)銷(xiāo),為具有多個(gè)功能單元的處理器提供指令級(jí)并行。也有利于指令流水線的調(diào)度。例如:
for?(i?=?1;?i?<=?60;?i++)?a[i]?=?a[i]?*?b?+?c;可以展開(kāi)成:
for?(i?=?1;?i?<=?60;?i+=3){a[i]?=?a[i]?*?b?+?c;a[i+1]?=?a[i+1]?*?b?+?c;a[i+2]?=?a[i+2]?*?b?+?c; }由于編譯器可能會(huì)對(duì)你的代碼進(jìn)行循環(huán)展開(kāi),因此JMH建議不要在你的測(cè)試方法中寫(xiě)任何循環(huán)。如果確實(shí)需要執(zhí)行循環(huán)計(jì)算,可以結(jié)合@BenchmarkMode(Mode.SingleShotTime)和@Measurement(batchSize = N)來(lái)達(dá)到同樣的效果。參考如下例子:
/**?Suppose?we?want?to?measure?how?much?it?takes?to?sum?two?integers:*/ int?x?=?1; int?y?=?2; /**?This?is?what?you?do?with?JMH.*/ @Benchmark @OperationsPerInvocation(100) public?int?measureRight()?{return?(x?+?y); }JMH可視化
在示例的main方法中指定了生成測(cè)試結(jié)果的輸出文件result.json,其中的內(nèi)容就是控制臺(tái)輸出的相關(guān)內(nèi)容以json格式存儲(chǔ)。
針對(duì)json格式的內(nèi)容,可以在其他網(wǎng)站上以圖表的形式可視化展示。
對(duì)應(yīng)網(wǎng)站,JMH Visual Chart(http://deepoove.com/jmh-visual-chart/)、JMH Visualizer(https://jmh.morethan.io/)。
展示效果如下圖:
img生成jar包執(zhí)行
對(duì)于大型的測(cè)試,一般會(huì)放在Linux服務(wù)器里去執(zhí)行。JMH官方提供了生成jar包的方式來(lái)執(zhí)行,在maven里增加如下插件:
<plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>2.4.1</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><finalName>jmh-demo</finalName><transformers><transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>org.openjdk.jmh.Main</mainClass></transformer></transformers></configuration></execution></executions></plugin> </plugins>執(zhí)行maven的命令生成可執(zhí)行jar包,并執(zhí)行:
mvn?clean?package java?-jar?target/jmh-demo.jar?JmhTest總結(jié)
一篇文章幾乎涵蓋了JMH各方面的知識(shí)點(diǎn),如果實(shí)踐中還沒(méi)運(yùn)用,趕緊用起來(lái)吧,你的專(zhuān)業(yè)水平將又提升那么一點(diǎn)。當(dāng)然,也可以收藏起來(lái),以備不時(shí)不需。
參考文章:
https://www.zhihu.com/question/276455629/answer/1259967560 https://www.cnblogs.com/silyvin/p/11736696.html https://blog.csdn.net/wangxuelei036/article/details/105240522 https://www.cnblogs.com/xiang--liu/p/9710143.html
往期推薦6種快速統(tǒng)計(jì)代碼執(zhí)行時(shí)間的方法,真香!(史上最全)
Oracle官方推薦的性能測(cè)試工具!簡(jiǎn)單、精準(zhǔn)又直觀!
鏈表竟然比數(shù)組慢了1000多倍?(動(dòng)圖+性能評(píng)測(cè))
關(guān)注我,每天陪你進(jìn)步一點(diǎn)點(diǎn)!
總結(jié)
以上是生活随笔為你收集整理的不要再用main方法测试代码性能了,用这款JDK自带工具的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 驳《阿里「Java开发手册」中的1个bu
- 下一篇: 重磅!阿里推出国产开源JDK!