深入分析JVM執(zhí)行引擎
一、閑聊
相信很多朋友在出國旅游,或者與外國友人溝通的過程中,都會(huì)遇到語言不通的煩惱。這時(shí)候我們就需要掌握對應(yīng)的外語或者擁有一部翻譯機(jī)。而筆者只會(huì)中文,所以需要借助一部翻譯器才能與不懂中文的外國友人交流。咱們的執(zhí)行引擎就類似于這部“翻譯機(jī)”。
二、概述
執(zhí)行引擎的作用就是將字節(jié)碼指令解釋或者編譯為對應(yīng)平臺上的本地機(jī)器指令。簡單來說,執(zhí)行引擎充當(dāng)了將高級語言翻譯為機(jī)器語言的翻譯者。對于Hotspot虛擬機(jī),執(zhí)行引擎中包含兩部分:解釋器和JIT編譯器(即時(shí)編譯器)。下圖是執(zhí)行引擎的原理:
三、解釋器
解釋器所承擔(dān)的角色就是一個(gè)運(yùn)行時(shí)翻譯者?,將字節(jié)碼文件中的內(nèi)容翻譯?為對應(yīng)平臺的本地機(jī)器碼指令。當(dāng)一條字節(jié)碼指令被解釋執(zhí)行后,接著再根據(jù)pc寄存器中記錄的下一條需要被執(zhí)行的字節(jié)碼指令執(zhí)行解釋操作。JVM解釋器一共有兩套,一套是遠(yuǎn)古的字節(jié)碼解釋器?,另一套是現(xiàn)在普遍使用的模板解釋器。
1、字節(jié)碼解釋器
字節(jié)碼解釋器在執(zhí)行過程中通過純軟件代碼模擬字節(jié)碼執(zhí)行,效率非常低。
2、模板解釋器
模板解釋器將每一條字節(jié)碼和一個(gè)模板函數(shù)關(guān)聯(lián),模板函數(shù)中直接產(chǎn)生這條字節(jié)碼指令執(zhí)行時(shí)的機(jī)器碼,從而提高了解釋器的性能。在常用的HotSpot VM中,解釋器主要由Interpreter模板和code模塊構(gòu)成。Interpreter模板:實(shí)現(xiàn)了解釋器的核心功能。code模塊:用于管理HotSpot VM在運(yùn)行時(shí)生成的本地機(jī)器碼指令。
四、即時(shí)編譯器(JIT編譯器)
即時(shí)編譯器的目的是避免函數(shù)被解釋執(zhí)行,而是將整個(gè)函數(shù)體編譯成機(jī)器碼指令,每次函數(shù)執(zhí)行時(shí),只執(zhí)行編譯后的機(jī)器碼即可,這種方式可以大大的提高效率。
1、熱點(diǎn)代碼及探測方式
當(dāng)然,是否需要JIT編譯器將字節(jié)碼直接編譯成對應(yīng)平臺的機(jī)器碼,需要根據(jù)代碼被調(diào)用的執(zhí)行頻率?而定。需要被JIT編譯器編譯成機(jī)器碼的字節(jié)碼,也稱為熱點(diǎn)代碼?,JIT編譯器會(huì)對熱點(diǎn)代碼做出深度優(yōu)化?,將其從字節(jié)碼編譯成機(jī)器碼,并緩存到方法區(qū)?,提高代碼的執(zhí)行效率。JIT編譯的方式發(fā)生在方法執(zhí)行過程中,因此也被稱之為_棧上替換_,或簡稱OSR(On Stack Replacement)編譯。通過熱點(diǎn)探測的方法,判斷一個(gè)方法被調(diào)用多少次,或循環(huán)體執(zhí)行多少次才可以達(dá)到閾值,進(jìn)行編譯。而Hotspot VM熱點(diǎn)探測的方式是基于計(jì)數(shù)器實(shí)現(xiàn)的。這種基于技術(shù)的熱點(diǎn)探測方式又分為兩種:1.方法調(diào)用計(jì)數(shù)器 2.回邊計(jì)數(shù)器
關(guān)于棧上替換這里筆者不展開贅述,有興趣的小伙伴可以自行了解下
1.1方法調(diào)用計(jì)數(shù)器
方法調(diào)用計(jì)數(shù)器用于統(tǒng)計(jì)方法調(diào)用次數(shù),它的默認(rèn)閾值是client模式下是1500次,在server模式下是10000次。超過這個(gè)閾值,就會(huì)觸發(fā)JIT編譯。當(dāng)然,這個(gè)閾值也可以通過修改虛擬機(jī)參數(shù)-XX:CompileThreshold來手動(dòng)指定。當(dāng)一個(gè)方法被調(diào)用的時(shí)候,會(huì)優(yōu)先檢查該方法是否被JIT編譯過,如果存在,則優(yōu)先使用編譯過的本地代碼來執(zhí)行,如果不存在,則將此方法的調(diào)用計(jì)數(shù)器加一,然后再判斷計(jì)數(shù)器的值是否超過配置的閾值。如果已經(jīng)超過了,就會(huì)向JIT編譯器提交一個(gè)該方法的編譯請求。下面是方法調(diào)用計(jì)數(shù)器執(zhí)行的流程圖:
關(guān)于方法調(diào)用計(jì)數(shù)器,如果不做任何設(shè)置,方法調(diào)用計(jì)數(shù)器統(tǒng)計(jì)的并不是方法被調(diào)用的絕對次數(shù),而是一個(gè)相對執(zhí)行的頻率。當(dāng)超過一定的時(shí)間限度,如果方法的調(diào)用次數(shù)仍然達(dá)不到閾值,那這個(gè)方法的調(diào)用計(jì)數(shù)器就會(huì)被減少一半,這個(gè)過程稱為方法調(diào)用計(jì)數(shù)器的熱度衰減?,而這段時(shí)間被稱作為該方法的半衰周期?。進(jìn)行熱度衰減的過程是虛擬機(jī)進(jìn)行垃圾回收的時(shí)候順便進(jìn)行的,舉手之勞而已??梢允褂锰摂M機(jī)參數(shù)-XX:-UseCounterDecay?來關(guān)閉熱度衰減。這樣的話,只要運(yùn)行時(shí)間足夠長,絕大部分方法都會(huì)被編譯成本地代碼。最后,還可以使用-XX:CounterHalfLifeTime參數(shù)設(shè)置半衰周期的時(shí)間,單位為秒。
1.2回邊計(jì)數(shù)器
它的作用是統(tǒng)計(jì)一個(gè)方法中循環(huán)體代碼執(zhí)行次數(shù),在字節(jié)碼中遇到控制流向后,跳轉(zhuǎn)的指令稱為“回邊”。顯然,建立回邊計(jì)數(shù)器統(tǒng)計(jì)的目的是為了觸發(fā)OSR編譯。下面是回邊計(jì)數(shù)器執(zhí)行的流程圖:
關(guān)于OSR編譯上文中有提到
2、即時(shí)編譯器分類
在Hotspot VM中,內(nèi)嵌有兩個(gè)JIT編譯器,分別為client compiler和server compiler,但是大多數(shù)情況下我們簡稱C1編譯器和C2編譯器??梢酝ㄟ^命令顯示的指定JVM在運(yùn)行時(shí)到底使用哪種JIT編譯器。
2.1 c1編譯器
指定Java虛擬機(jī)運(yùn)行在client模式下,使用C1編譯器。C1編譯器會(huì)對字節(jié)碼進(jìn)行簡單和可靠的優(yōu)化,耗時(shí)短。以達(dá)到更快的編譯速度,但是編譯后的代碼執(zhí)行速度相對慢。C1編譯器主要有方法內(nèi)聯(lián),去虛擬化,冗余消除。
方法內(nèi)聯(lián):將引用的函數(shù)代碼編譯到引用點(diǎn)處,這樣可以減少棧幀的生成,減少參數(shù)傳遞以及跳轉(zhuǎn)過程。
去虛擬化:對唯一實(shí)現(xiàn)的類進(jìn)行內(nèi)聯(lián)。
冗余消除:在運(yùn)行期間把一些不會(huì)執(zhí)行的代碼疊掉。
2.2 c2編譯器
指定Java虛擬機(jī)運(yùn)行在server模式下,使用C2編譯器。C2編譯器對代碼優(yōu)化時(shí)間長,編譯時(shí)間也長。但是編譯后的代碼執(zhí)行速度比較快。C2的優(yōu)化主要在全局層面,逃逸分析是優(yōu)化的基礎(chǔ)?;谔右莘治?,C2上有如下幾種優(yōu)化:
標(biāo)量替換:用標(biāo)量值代替聚合對象的屬性值。
棧上分配:對于未逃逸的對象分配在棧上而不是堆上。
同步消除:清除同步操作,通常指synchronized。
2.3 Graal編譯器
JDK10起,在C1編譯器和C2編譯器之后,HotSpot VM新增了一個(gè)Graal即時(shí)編譯器。編譯效果短短幾年的時(shí)間就追平了C2編譯器。目前,帶著“實(shí)驗(yàn)狀態(tài)”標(biāo)簽,需要使用開關(guān)參數(shù)-XX:+UnlockExperimentalVMOptions,-XX:+UseJVMCICompiler去激活這個(gè)編譯器,才能使用。
五、解釋器和JIT并存
為什么需要解釋器和JIT并存,原因有幾點(diǎn):
當(dāng)程序啟動(dòng)的時(shí)候,解釋器可以馬上發(fā)揮作用,省去編譯的時(shí)間。
編譯器想要執(zhí)行,需要把字節(jié)碼編譯成本地機(jī)器碼,并且緩存編譯后的機(jī)器碼,編譯需要一定的時(shí)間。
編譯后的本地機(jī)器碼,執(zhí)行效率高。所以,在兩種并存的模式下,解釋器首先發(fā)揮作用,而不必等到即時(shí)編譯器全部編譯完再執(zhí)行,這樣可以省去不必要的編譯時(shí)間。
隨著程序繼續(xù)不斷運(yùn)行,編譯器發(fā)揮作用,根據(jù)熱點(diǎn)探測功能,把越來越多的字節(jié)碼編譯成本地機(jī)器碼,獲得更高的執(zhí)行效率。
六、執(zhí)行引擎執(zhí)行程序的方式
在默認(rèn)的情況下,HotSpot VM采用的是解釋器和JIT編譯器并存的架構(gòu),當(dāng)然讀者可以根據(jù)具體的應(yīng)用場景,通過虛擬機(jī)參數(shù),為虛擬機(jī)指定在運(yùn)行時(shí)到底是完全采用解釋器執(zhí)行,還是完全采用即時(shí)編譯器執(zhí)行。
-Xint:完全采用解釋器模式執(zhí)行程序
-XComp:完全采用即時(shí)編譯器模式執(zhí)行程序。如果即時(shí)編譯器出現(xiàn)問題,解釋器會(huì)介入執(zhí)行;
-Xmixed:采用解釋器+即時(shí)編譯器的混合模式共同執(zhí)行程序,HotStop VM默認(rèn)就是這個(gè)模式。
七、參考源碼
編程文檔:https://gitee.com/cicadasmile/butte-java-note
應(yīng)用倉庫:https://gitee.com/cicadasmile/butte-flyer-parent