小师妹学JVM之:JIT中的LogCompilation
文章目錄
- 簡(jiǎn)介
- LogCompilation簡(jiǎn)介
- LogCompilation的使用
- 解析LogCompilation文件
- 總結(jié)
簡(jiǎn)介
我們知道在JVM中為了加快編譯速度,引入了JIT即時(shí)編譯的功能。那么JIT什么時(shí)候開始編譯的,又是怎么編譯的,作為一個(gè)高傲的程序員,有沒有辦法去探究JIT編譯的秘密呢?答案是有的,今天和小師妹一起帶大家來看一看這個(gè)編譯背后的秘密。
更多精彩內(nèi)容且看:
- 區(qū)塊鏈從入門到放棄系列教程-涵蓋密碼學(xué),超級(jí)賬本,以太坊,Libra,比特幣等持續(xù)更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續(xù)更新
- Spring 5.X系列教程:滿足你對(duì)Spring5的一切想象-持續(xù)更新
- java程序員從小工到專家成神之路(2020版)-持續(xù)更新中,附詳細(xì)文章教程
LogCompilation簡(jiǎn)介
小師妹:F師兄,JIT這么神器,但是好像就是一個(gè)黑盒子,有沒有辦法可以探尋到其內(nèi)部的本質(zhì)呢?
追求真理和探索精神是我們作為程序員的最大優(yōu)點(diǎn),想想如果沒有玻爾關(guān)于原子結(jié)構(gòu)的新理論,怎么會(huì)有原子體系的突破,如果沒有海森堡的矩陣力學(xué),怎么會(huì)有量子力學(xué)的建立?
JIT的編譯日志輸出很簡(jiǎn)單,使用 -XX:+LogCompilation就夠了。
如果要把日志重定向到一個(gè)日志文件中,則可以使用-XX:LogFile= 。
但是要開啟這些分析的功能,又需要使用-XX:+UnlockDiagnosticVMOptions。 所以總結(jié)一下,我們需要這樣使用:
-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:LogFile=www.flydean.com.logLogCompilation的使用
根據(jù)上面的介紹,我們現(xiàn)場(chǎng)來生成一個(gè)JIT的編譯日志,為了體現(xiàn)出專業(yè)性,這里我們需要使用到JMH來做性能測(cè)試。
JMH的全稱是Java Microbenchmark Harness,是一個(gè)open JDK中用來做性能測(cè)試的套件。該套件已經(jīng)被包含在了JDK 12中。
如果你使用的不是JDK 12,那么需要添加如下依賴:
<dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-core</artifactId><version>1.19</version> </dependency> <dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-generator-annprocess</artifactId><version>1.19</version> </dependency>更多詳情可以參考我之前寫的: 在java中使用JMH(Java Microbenchmark Harness)做性能測(cè)試一文。
之前有的朋友說,代碼也用圖片,看起來好看,從本文之后,我們會(huì)盡量把代碼也轉(zhuǎn)成圖片來展示:
看完我的JMH的介紹,上面的例子應(yīng)該很清楚了,主要就是做一個(gè)累加操作,然后warmup 5輪,測(cè)試5輪。
在@Fork注解里面,我們可以配置jvm的參數(shù),為什么我注釋掉了呢?因?yàn)槲野l(fā)現(xiàn)在jvmArgsPrepend中的-XX:LogFile是不生效的。
沒辦法,我只好在運(yùn)行配置中添加:
運(yùn)行之后,你就可以得到輸出的編譯日志文件。
解析LogCompilation文件
小師妹:F師兄,我看了一下生成的文件好復(fù)雜啊,用肉眼能看得明白嗎?
別怕,只是內(nèi)容的多一點(diǎn),如果我們細(xì)細(xì)再細(xì)細(xì)的分析一下,你會(huì)發(fā)現(xiàn)其實(shí)它真的非常非常…復(fù)雜!
其實(shí)寫點(diǎn)簡(jiǎn)單的小白文不好嗎?為什么要來分析這么復(fù)雜,又沒人看,看了也沒人懂的JVM底層…
大概,這就是專業(yè)吧!
LogCompilation文件其實(shí)是xml格式的,我們現(xiàn)在來大概分析一下,它的結(jié)構(gòu),讓大家下次看到這個(gè)文件也能夠大概了解它的重點(diǎn)。
首先最基本的信息就是JVM的信息,包括JVM的版本,JVM運(yùn)行的參數(shù),還有一些properties屬性。
我們收集到的日志其實(shí)是分兩類的,第一類是應(yīng)用程序本身的的編譯日志,第二類就是編譯線程自己內(nèi)部產(chǎn)生的日志。
第二類的日志會(huì)以hs_c*.log的格式存儲(chǔ),然后在JVM退出的時(shí)候,再將這些文件跟最終的日志輸出文件合并,生成一個(gè)整體的日志文件。
比如下面的兩個(gè)就是編譯線程內(nèi)部的日志:
<thread_logfile thread='22275' filename='/var/folders/n5/217y_bgn49z18zvjch907xb00000gp/T//hs_c22275_pid83940.log'/> <thread_logfile thread='41731' filename='/var/folders/n5/217y_bgn49z18zvjch907xb00000gp/T//hs_c41731_pid83940.log'/>上面列出了編譯線程的id=22275,如果我們順著22275找下去,則可以找到具體編譯線程的日志:
<compilation_log thread='22275'> ... </compilation_log>上面由compilation_log圍起來的部分就是編譯日志了。
接下來的部分表示,編譯線程開始執(zhí)行了,其中stamp表示的是啟動(dòng)時(shí)間,下圖列出了一個(gè)完整的編譯線程的日志:
<start_compile_thread name='C2 CompilerThread0' thread='22275' process='83940' stamp='0.058'/>接下來描述的是要編譯的方法信息:
<task compile_id='10' method='java.lang.Object <init> ()V' bytes='1' count='1409' iicount='1409' stamp='0.153'>上面列出了要編譯的方法名,compile_id表示的是系統(tǒng)內(nèi)部分配的編譯id,bytes是方法中的字節(jié)數(shù),count表示的是該方法的調(diào)用次數(shù),注意,這里的次數(shù)并不是方法的真實(shí)調(diào)用次數(shù),只能做一個(gè)估計(jì)。
iicount是解釋器被調(diào)用的次數(shù)。
task執(zhí)行了,自然就會(huì)執(zhí)行完成,執(zhí)行完成的內(nèi)容是以task_done標(biāo)簽來表示的:
<task_done success='1' nmsize='120' count='1468' stamp='0.155'/>其中success表示是否成功執(zhí)行,nmsize表示編譯器編譯出來的指令大小,以byte為單位。如果有內(nèi)聯(lián)的話,還有個(gè)inlined_bytes屬性,表示inlined的字節(jié)個(gè)數(shù)。
<type id='1025' name='void'/>type表示的是方法的返回類型。
<klass id='1030' name='java.lang.Object' flags='1'/>klass表示的是實(shí)例和數(shù)組類型。
<method id='1148' holder='1030' name='<init>' return='1025' flags='1' bytes='1' compile_id='1' compiler='c1' level='3' iicount='1419'/>method表示執(zhí)行的方法,holder是前面的klass的id,表示的是定義該方法的實(shí)例或者數(shù)組對(duì)象。method有名字,有
return,return對(duì)應(yīng)的是上面的type。
flags表示的是方法的訪問權(quán)限。
接下來是parse,是分析階段的日志:
<parse method='1148' uses='1419.000000' stamp='0.153'>上面有parse的方法id。uses是使用次數(shù)。
<bc code='177' bci='0'/>bc是byte Count的縮寫,code是byte的個(gè)數(shù),bci是byte code的索引。
<dependency type='no_finalizable_subclasses' ctxk='1030'/>dependency分析的是類的依賴關(guān)系,type表示的是什么類型的依賴,ctkx是依賴的context class。
我們注意有的parse中,可能會(huì)有uncommon_trap:
<uncommon_trap bci='10' reason='unstable_if' action='reinterpret' debug_id='0' comment='taken never'/>怎么理解uncommon_trap呢?字面上意思就是捕獲非常用的代碼,就是說在解析代碼的過程中發(fā)現(xiàn)發(fā)現(xiàn)這些代碼是uncommon的,然后解析產(chǎn)生一個(gè)uncommon_trap,不再繼續(xù)進(jìn)行了。
它里面有兩個(gè)比較重要的字段,reason表示的是被標(biāo)記為uncommon_trap的原因。action表示的出發(fā)uncommon_trap的事件。
有些地方還會(huì)有call:
<call method='1150' count='5154' prof_factor='1.000000' inline='1'/>call的意思是,在該代碼中將會(huì)調(diào)用其他的方法。count是執(zhí)行次數(shù)。
總結(jié)
復(fù)雜的編譯日志終于講完了,可能講的并不是很全,還有一些其他情況這里并沒有列出來,后面如果遇到了,我再添加進(jìn)去。
本文的例子https://github.com/ddean2009/learn-java-base-9-to-20
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/jvm-jit-logcompilation/
本文來源:flydean的博客
歡迎關(guān)注我的公眾號(hào):程序那些事,更多精彩等著您!
超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的小师妹学JVM之:JIT中的LogCompilation的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小师妹学JVM之:JDK14中JVM的性
- 下一篇: 从印度兵力分布聊聊Mybatis中#和$