執(zhí)行引擎
概述
●?執(zhí)行引擎是Java虛擬機(jī)核心的組成部分之一镀岛。
●?“虛擬機(jī)”是一個(gè)相對(duì)于“物理機(jī)”的概念,這兩種機(jī)器都有代碼執(zhí)行能力挠乳,其區(qū)別是物理機(jī)的執(zhí)行引擎是直接建立在處理器饭豹、緩存、指令集和操作系統(tǒng)層面上的,而虛擬機(jī)的執(zhí)行引擎則是由軟件自行實(shí)現(xiàn)的,因此可以不受物理?xiàng)l件制約地定制指令集與執(zhí)行引擎的結(jié)構(gòu)體系,能夠執(zhí)行那些不被硬件直接支持的指令集格式被环。
JVM的主要任務(wù)是負(fù)責(zé)裝載字節(jié)碼到其內(nèi)部,但字節(jié)碼并不能夠直接運(yùn)行在操作系統(tǒng)之上详幽,因?yàn)樽止?jié)碼指令并非等價(jià)于本地機(jī)器指令筛欢,它內(nèi)部包含的僅僅只是一些能夠被JVM所識(shí)別的字節(jié)碼指令浸锨、符號(hào)表,以及其他輔助信息版姑。
那么柱搜,如果想要讓一個(gè)Java程序運(yùn)行起來,執(zhí)行引擎(Execution Engine)的任務(wù)就是將字節(jié)碼指令解釋/編譯為對(duì)應(yīng)平臺(tái)上的本地機(jī)器指令才可以剥险。簡(jiǎn)單來說聪蘸,JVM中的執(zhí)行引擎充當(dāng)了將高級(jí)語(yǔ)言翻譯為機(jī)器語(yǔ)言的譯者。
工作過程
1表制、執(zhí)行引擎在執(zhí)行的過程中究竟需要執(zhí)行什么樣的字節(jié)碼指令完全依賴于PC寄存器健爬。
2、每當(dāng)執(zhí)行完一項(xiàng)指令操作后么介,PC寄存器就會(huì)更新下一條需要被執(zhí)行的指令地址娜遵。
3、 當(dāng)然方法在執(zhí)行的過程中壤短,執(zhí)行引擎有可能會(huì)通過存儲(chǔ)在局部變量表中的對(duì)象引用準(zhǔn)確定位到存儲(chǔ)在Java堆區(qū)中的對(duì)象實(shí)例信息设拟,以及通過對(duì)象頭中的元數(shù)據(jù)指針定位到目標(biāo)對(duì)象的類型信息。
Java代碼編譯和執(zhí)行過程
大部分的程序代碼轉(zhuǎn)換成物理機(jī)的目標(biāo)代碼或虛擬機(jī)能執(zhí)行的指令集之前鸽扁,都需要經(jīng)過上圖中的各個(gè)步驟。
Java代碼編譯是由Java源碼編譯器來完成镶骗,流程圖如下所示:
什么是解釋器( Interpreter),什么是JIT編譯器鼎姊?
解釋器:當(dāng)Java虛擬機(jī)啟動(dòng)時(shí)會(huì)根據(jù)預(yù)定義的規(guī)范對(duì)字節(jié)碼采用逐行解釋的方式執(zhí)行骡和,將每條字節(jié)碼文件中的內(nèi)容“翻譯”為對(duì)應(yīng)平臺(tái)的本地機(jī)器指令執(zhí)行。
JIT (Just In Time Compiler) 編譯器:就是虛擬機(jī)將源代碼直接編譯成和本地機(jī)器平臺(tái)相關(guān)的機(jī)器語(yǔ)言相寇。
為什么說Java是半編譯半解釋型語(yǔ)言
JDK1. 0時(shí)代慰于,將Java語(yǔ)言定位為“解釋執(zhí)行”還是比較準(zhǔn)確的。再后來唤衫,Java也發(fā)展出可以直接生成本地代碼的編譯器婆赠。
現(xiàn)在JVM在執(zhí)行Java代碼的時(shí)候,通常都會(huì)將解釋執(zhí)行與編譯執(zhí)行二者結(jié)合起來進(jìn)行佳励。
機(jī)器碼休里、指令、匯編語(yǔ)言
機(jī)器碼
●?各種用二進(jìn)制編碼方式表示的指令赃承,叫做機(jī)器指令碼妙黍。開始,人們就用它采編寫程序瞧剖,這就是機(jī)器語(yǔ)言拭嫁。
●?機(jī)器語(yǔ)言雖然能夠被計(jì)算機(jī)理解和接受可免,但和人們的語(yǔ)言差別太大,不易被人們理解和記憶做粤,并且用它編程容易出差錯(cuò)浇借。
●?用它編寫的程序- -經(jīng)輸入計(jì)算機(jī),CPU直接讀取運(yùn)行驮宴,因此和其他語(yǔ)言編的程序相比逮刨,執(zhí)行速度最快。
●?機(jī)器指令與CPU緊密相關(guān)堵泽,所以不同種類的CPU所對(duì)應(yīng)的機(jī)器指令也就不同修己。
指令
●?由于機(jī)器碼是有0和1組成的二進(jìn)制序列,可讀性實(shí)在太差迎罗,于是人們發(fā)明了指令睬愤。
●?指令就是把機(jī)器碼中特定的0和1序列,簡(jiǎn)化成對(duì)應(yīng)的指令(一般為英文簡(jiǎn)寫纹安,如mov尤辱、inc等),可讀性稍好
●?由于不同的硬件平臺(tái)厢岂,執(zhí)行同一個(gè)操作光督,對(duì)應(yīng)的機(jī)器碼可能不同,所以不同的硬件平臺(tái)的同一種指令(比如mov)塔粒,對(duì)應(yīng)的機(jī)器碼也可能不同结借。
指令集
●?不同的硬件平臺(tái),各自支持的指令卒茬,是有差別的船老。因此每個(gè)平臺(tái)所支持的指令,稱之
為對(duì)應(yīng)平臺(tái)的指令集。
●?如常見的
???x86指令集圃酵,對(duì)應(yīng)的是x86架構(gòu)的平臺(tái)
???ARM指令集柳畔,對(duì)應(yīng)的是ARM架構(gòu)的平臺(tái)
匯編語(yǔ)言
●?由于指令的可讀性還是太差,于是人們又發(fā)明了匯編語(yǔ)言郭赐。
●?在匯編語(yǔ)言中薪韩,用助記符(Mnemonics)代替機(jī)器指令的操作碼,用地址符號(hào)(Symbol)或標(biāo)號(hào)(Label)代替指令或操作數(shù)的地址捌锭。
●?在不同的硬件平臺(tái)躬存,匯編語(yǔ)言對(duì)應(yīng)著不同的機(jī)器語(yǔ)言指令集,通過匯編過
程轉(zhuǎn)換成機(jī)器指令舀锨。
???由于計(jì)算機(jī)只認(rèn)識(shí)指令碼岭洲,所以用匯編語(yǔ)言編寫的程序還必須翻譯成機(jī)器指令碼,計(jì)算機(jī)才能識(shí)別和執(zhí)行坎匿。
高級(jí)語(yǔ)言
●?為了使計(jì)算機(jī)用戶編程序更容易些盾剩,后來就出現(xiàn)了各種高級(jí)計(jì)算機(jī)語(yǔ)言雷激。高級(jí)語(yǔ)言比機(jī)器語(yǔ)言、匯編語(yǔ)言更接近人的語(yǔ)言
●?當(dāng)計(jì)算機(jī)執(zhí)行高級(jí)語(yǔ)言編寫的程序時(shí)告私,仍然需要把程序解釋和編譯成機(jī)器的指令碼屎暇。完成這個(gè)過程的程序就叫做解釋程序或編譯程序。
字節(jié)碼
●?字節(jié)碼是一種中間狀態(tài)(中間碼)的二進(jìn)制代碼(文件)驻粟,它比機(jī)器碼更抽象根悼,需要直譯器轉(zhuǎn)譯后才能成為機(jī)器碼
●?字節(jié)碼主要為了實(shí)現(xiàn)特定軟件運(yùn)行和軟件環(huán)境、與硬件環(huán)境無關(guān)蜀撑。
●?字節(jié)碼的實(shí)現(xiàn)方式是通過編譯器和虛擬機(jī)器挤巡。編譯器將源碼編譯成字節(jié)碼,特定平臺(tái),上的虛擬機(jī)器將字節(jié)碼轉(zhuǎn)譯為可以直接執(zhí)行的指令酷麦。
???字節(jié)碼的典型應(yīng)用為Java bytecode矿卑。
C、C++源程序執(zhí)行過程:
編譯過程又可以分成兩個(gè)階段:編譯和匯編沃饶。
●?編譯過程:是讀取源程序(字符流)母廷,對(duì)之進(jìn)行詞法和語(yǔ)法的分析,將高級(jí)語(yǔ)言指令轉(zhuǎn)換為功能等效的匯編代碼
●?匯編過程:實(shí)際上指把匯編語(yǔ)言代碼翻譯成目標(biāo)機(jī)器指令的過程糊肤。
解釋器
概述
JVM設(shè)計(jì)者們的初衷僅僅只是單純地為了滿足Java程序?qū)崿F(xiàn)跨平臺(tái)特性琴昆,因此避免采用靜態(tài)編譯的方式直接生成本地機(jī)器指令,從而誕生了實(shí)現(xiàn)解釋器在運(yùn)行時(shí)采用逐行解釋字節(jié)碼執(zhí)行程序的想法馆揉。
解釋器工作機(jī)制(或工作任務(wù))
●?解釋器真正意義上所承擔(dān)的角色就是-一個(gè)運(yùn)行時(shí)“翻譯者”业舍,將字節(jié)碼文件中的內(nèi)容“翻譯”為對(duì)應(yīng)平臺(tái)的本地機(jī)器指令執(zhí)行。
●?當(dāng)一條字節(jié)碼指令被解釋執(zhí)行完成后把介,接著再根據(jù)PC寄存器中記錄的下一條需要被執(zhí)行的字節(jié)碼指令執(zhí)行解釋操作勤讽。
分類
在Java的發(fā)展歷史里蟋座,一共有兩套解釋執(zhí)行器拗踢,即古老的字節(jié)碼解釋器、現(xiàn)在普遍使用的模板解釋器向臀。
●?字節(jié)碼解釋器在執(zhí)行時(shí)通過純軟件代碼模擬字節(jié)碼的執(zhí)行巢墅,效率非常低下。
●?而模板解釋器將每一條字節(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ī)器指令
現(xiàn)狀
●?由于解釋器在設(shè)計(jì)和實(shí)現(xiàn)上非常簡(jiǎn)單,因此除了Java語(yǔ)言之外舒帮,還有許多高級(jí)語(yǔ)言同樣也是基于解釋器執(zhí)行的会喝,比如Python陡叠、 Perl、Ruby等肢执。但是在今天枉阵,基于解釋器執(zhí)行已經(jīng)淪落為低效的代名詞,并且時(shí)常被一些C/C++程序員所調(diào)侃预茄。
●?為了解決這個(gè)問題兴溜,JVM平臺(tái)支持一種叫作即時(shí)編譯的技術(shù)。即時(shí)編譯的目的是避免函數(shù)被解釋執(zhí)行耻陕,而是將整個(gè)函數(shù)體編譯成為機(jī)器碼拙徽,每次函數(shù)執(zhí)行時(shí),只執(zhí)行編譯后的機(jī)器碼即可淮蜈,這種方式可以使執(zhí)行效率大幅度提升斋攀。
●?不過無論如何,基于解釋器的執(zhí)行模式仍然為中間語(yǔ)言的發(fā)展做出了不可
磨滅的貢獻(xiàn)梧田。
JIT編譯器
Java代碼的執(zhí)行分類
●?第一種是將源代碼編譯成字節(jié)碼文件淳蔼,然后在運(yùn)行時(shí)通過解釋器將字節(jié)碼文件轉(zhuǎn)為機(jī)器碼執(zhí)行
●?第二種是編譯執(zhí)行(直接編譯成機(jī)器碼)。現(xiàn)代虛擬機(jī)為了提高執(zhí)行效率裁眯,會(huì)使用即時(shí)編譯技術(shù)(JIT,Just In Time) 將方法編譯成機(jī)器碼后再執(zhí)行
●?HotSpotVM是目前市面上高性能虛擬機(jī)的代表作之一鹉梨。它采用解釋器與即時(shí)編譯器并存的架構(gòu)。在Java虛擬機(jī)運(yùn)行時(shí)穿稳,解釋器和即時(shí)編譯器能夠相互協(xié)作存皂,各自取長(zhǎng)補(bǔ)短,盡力去選擇最合適的方式來權(quán)衡編譯本地代碼的時(shí)間和直接解釋執(zhí)行代碼的時(shí)間逢艘。
●?在今天旦袋,Java程序的運(yùn)行性能早已脫胎換骨,已經(jīng)達(dá)到了可以和C/C++程序一較高下的地步它改。
問題
有些開發(fā)人員會(huì)感覺到詫異疤孕,既然HotSpot VM中已經(jīng)內(nèi)置JIT編譯器了,那么為什么還需要再使用解釋器來“拖累”程序的執(zhí)行性能呢央拖?比如JRockit VM內(nèi)部就不包含解釋器祭阀,字節(jié)碼全部都依靠即時(shí)編譯器編譯后執(zhí)行。
首先明確:
當(dāng)程序啟動(dòng)后鲜戒,解釋器可以馬上發(fā)揮作用专控,省去編譯的時(shí)間,立即執(zhí)行遏餐。編譯器要想發(fā)揮作用伦腐,把代碼編譯成本地代碼,需要一定的執(zhí)行時(shí)間失都。 但編譯為本地代碼后柏蘑,執(zhí)行效率高颖系。
所以:
盡管JRockitVM中程序的執(zhí)行性能會(huì)非常高效,但程序在啟動(dòng)時(shí)必然需要花費(fèi)更長(zhǎng)的時(shí)間來進(jìn)行編譯辩越。對(duì)于服務(wù)端應(yīng)用來說嘁扼,啟動(dòng)時(shí)間并非是關(guān)注重點(diǎn),但對(duì)于那些看中啟動(dòng)時(shí)間的應(yīng)用場(chǎng)景而言黔攒,或許就需要采用解釋器與即時(shí)編譯器并存的架構(gòu)來?yè)Q取一個(gè)平衡點(diǎn)趁啸。在此模式下,當(dāng)Java虛擬器啟動(dòng)時(shí)督惰,解釋器可以首先發(fā)揮作用不傅,而不必等待即時(shí)編譯器全部編譯完成后再執(zhí)行,這樣可以省去許多不必要的編譯時(shí)間赏胚。隨著時(shí)間的推移访娶,編譯器發(fā)揮作用,把越來越多的代碼編譯成本地代碼觉阅,獲得更高的執(zhí)行效率崖疤。
同時(shí),解釋執(zhí)行在編譯器進(jìn)行激進(jìn)優(yōu)化不成立的時(shí)候典勇,作為編譯器的“逃生門”劫哼。
Hotspot JVM的執(zhí)行方式
當(dāng)虛擬機(jī)啟動(dòng)的時(shí)候,解釋器可以首先發(fā)揮作用割笙,而不必等待即時(shí)編譯器全部編譯完成再執(zhí)行权烧,這樣可以省去許多不必要的編譯時(shí)間。并且隨著程序運(yùn)行時(shí)間的推移伤溉,即時(shí)編譯器逐漸發(fā)揮作用般码,根據(jù)熱點(diǎn)探測(cè)功能,將有價(jià)值的字節(jié)碼編譯為本地機(jī)器指令乱顾,以換取更高的程序執(zhí)行效率板祝。
案例
注意解釋執(zhí)行與編譯執(zhí)行在線上環(huán)境微妙的辯證關(guān)系。機(jī)器在熱機(jī)狀態(tài)可以承受的負(fù)載要大于冷機(jī)狀態(tài)糯耍。如果以熱機(jī)狀態(tài)時(shí)的流量進(jìn)行切流扔字,可能使處于冷機(jī)狀態(tài)的服務(wù)器因無法承載流量而假死囊嘉。
在生產(chǎn)環(huán)境發(fā)布過程中温技,以分批的方式進(jìn)行發(fā)布,根據(jù)機(jī)器數(shù)量劃分成多個(gè)批次扭粱,每個(gè)批次的機(jī)器數(shù)至多占到整個(gè)集群的1/8舵鳞。曾經(jīng)有這樣的故障案例:某程序員在發(fā)布平臺(tái)進(jìn)行(分批發(fā)布,在輸入發(fā)布總批數(shù)時(shí)琢蛤,誤填寫成分為兩批發(fā)布蜓堕。如果是熱機(jī)狀態(tài)抛虏,在正常情況下一半的機(jī)器可以勉強(qiáng)承載流量,但由于剛啟動(dòng)的JVM均是解釋執(zhí)行套才,還沒有進(jìn)行熱點(diǎn)代碼統(tǒng)計(jì)和JIT動(dòng)態(tài)編譯迂猴,導(dǎo)致機(jī)器啟動(dòng)之后,當(dāng)前1/2發(fā)布成功的服務(wù)器馬上全部宕機(jī)背伴,此故障說明了JIT的存在沸毁。——阿里團(tuán)隊(duì)
概念解釋
●?Java語(yǔ)言的“編譯期”其實(shí)是一 段“不確定”的操作過程傻寂,因?yàn)樗赡苁侵敢粋€(gè)前端編譯器(其實(shí)叫“ 編譯器的前端”更準(zhǔn)確一 些)把java文件轉(zhuǎn)變成class文件的過程息尺。
●?也可能是指虛擬機(jī)的后端運(yùn)行期編譯器(JIT編譯器,Just In Time Compiler)把字節(jié)碼轉(zhuǎn)變成機(jī)器碼的過程疾掰。
●?還可能是指使用靜態(tài)提前編譯器(AOT 編譯器搂誉,Ahead Of Time Compiler)直接把java文件編譯成本地機(jī)器代碼的過程。
熱點(diǎn)代碼及探測(cè)方式
當(dāng)然是否需要啟動(dòng)JIT編譯器將字節(jié)碼直接編譯為對(duì)應(yīng)平臺(tái)的本地機(jī)器指令静檬,則需要根據(jù)代碼被調(diào)用執(zhí)行的頻率而定炭懊。關(guān)于那些需要被編譯為本地代碼的字節(jié)碼,也被稱之為“熱點(diǎn)代碼”拂檩,JIT行編譯器在運(yùn)時(shí)會(huì)針對(duì)那些頻繁被調(diào)用的“熱點(diǎn)代碼”做出深度優(yōu)化凛虽,將其直接編譯為對(duì)應(yīng)平臺(tái)的本地機(jī)器指令,以此提升Java程序的執(zhí)行性能广恢。
●?一個(gè)被多次調(diào)用的方法凯旋,或者是一個(gè)方法體內(nèi)部循環(huán)次數(shù)較多的循環(huán)體都可以被稱之為“熱點(diǎn)代碼”,因此都可以通過JIT編譯器編譯為本地機(jī)器指令钉迷。由于這種編譯方式發(fā)生在方法的執(zhí)行過程中至非,因此也被稱之為棧上替換,或簡(jiǎn)稱為OSR (On Stack Replacement)編譯糠聪。
●?一個(gè)方法究竟要被調(diào)用多少次荒椭,或者一個(gè)循環(huán)體究竟需要執(zhí)行多少次循環(huán)才可以達(dá)到這個(gè)標(biāo)準(zhǔn)?必然需要一個(gè)明確的閾值,JIT編譯器才會(huì)將這些“熱點(diǎn)代碼”編譯為本地機(jī)器指令執(zhí)行舰蟆。這里主要依靠熱點(diǎn)探測(cè)功能趣惠。
●?目前HotSpot VM所采用的熱點(diǎn)探測(cè)方式是基于計(jì)數(shù)器的熱點(diǎn)探測(cè)。
●?采用基于計(jì)數(shù)器的熱點(diǎn)探測(cè)身害,HotSpot VM將會(huì)為每一個(gè) 方法都建立2個(gè)不同類型的計(jì)數(shù)器味悄,分別為方法調(diào)用計(jì)數(shù)器(Invocation Counter) 和回邊計(jì)數(shù)器(Back Edge Counter) 。
???方法調(diào)用計(jì)數(shù)器用于統(tǒng)計(jì)方法的調(diào)用次數(shù)
???回邊計(jì)數(shù)器則用于統(tǒng)計(jì)循環(huán)體執(zhí)行的循環(huán)次數(shù)
方法調(diào)用計(jì)數(shù)器
●?這個(gè)計(jì)數(shù)器就用于統(tǒng)計(jì)方法被調(diào)用的次數(shù)塌鸯,它的默認(rèn)閾值在Client 模式下是 1500 次侍瑟,在Server 模式下是10000次。超過這個(gè)閾值, 就會(huì)觸發(fā)JIT編譯涨颜。
●?這個(gè)閾值可以通過虛擬機(jī)參數(shù)-XX:CompileThreshold來人為設(shè)定费韭。
●?當(dāng)一個(gè)方法被調(diào)用時(shí),會(huì)先檢查該方法是否存在被JIT編譯過的版本庭瑰,如果存在星持,則優(yōu)先使用編譯后的本地代碼來執(zhí)行。如果不存在已被編譯過的版本弹灭,則將此方法的調(diào)用計(jì)數(shù)器值加1钉汗,然后判斷方法調(diào)用計(jì)數(shù)器與回邊計(jì)數(shù)器值之和是否超過方法調(diào)用計(jì)數(shù)器的閾值。如果已超過閾值鲤屡,那么將會(huì)向即時(shí)編譯器提交一個(gè)該方法的代碼編譯請(qǐng)求损痰。
熱度衰減
●?如果不做任何設(shè)置,方法調(diào)用計(jì)數(shù)器統(tǒng)計(jì)的并不是方法被調(diào)用的絕對(duì)次數(shù)酒来,而是一個(gè)相對(duì)的執(zhí)行頻率卢未,即一段時(shí)間之內(nèi)方法被調(diào)用的次數(shù)。當(dāng)超過一定的時(shí)間限度堰汉,如果方法的調(diào)用次數(shù)仍然不足以讓它提交給即時(shí)編譯器編譯辽社,那這個(gè)方法的調(diào)用計(jì)數(shù)器就會(huì)被減少一半,這個(gè)過程稱為方法調(diào)用計(jì)數(shù)器熱度的衰減(Counter Decay)翘鸭,而這段時(shí)間就稱為此方法統(tǒng)計(jì)的半衰周期(Counter Half Life Time)滴铅。
●?進(jìn)行熱度衰減的動(dòng)作是在虛擬機(jī)進(jìn)行垃圾收集時(shí)順便進(jìn)行的就乓,可以使用虛擬機(jī)參數(shù)-xx:-UseCounterDecay來關(guān)閉熱度衰減汉匙,讓方法計(jì)數(shù)器統(tǒng)計(jì)方法調(diào)用的絕對(duì)次數(shù),這樣生蚁,只要系統(tǒng)運(yùn)行時(shí)間足夠長(zhǎng),絕大部分方法都會(huì)被編譯成本地代碼邦投。
●?另外,可以使用-xx:CounterHalfLifeTime參數(shù)設(shè)置半衰周期的時(shí)間屯援,單位是秒念脯。
回邊計(jì)數(shù)器
它的作用是統(tǒng)計(jì)一個(gè)方法中循環(huán)體代碼執(zhí)行的次數(shù),在字節(jié)碼中遇到控制流向后跳轉(zhuǎn)的指令稱為“回邊” (Back Edge)徘铝。顯然惯吕,建立回邊計(jì)數(shù)器統(tǒng)計(jì)的目的就是為了觸發(fā)OSR編譯。
Hotspot VM可以設(shè)置程序執(zhí)行方式
缺省情況下HotSpot VM是采用解釋器與即時(shí)編譯器并存的架構(gòu)淹魄,當(dāng)然開發(fā)人員可以根據(jù)具體的應(yīng)用場(chǎng)景,通過命令顯式地為Java虛擬機(jī)指定在運(yùn)行時(shí)到底是完全采用解釋器執(zhí)行堡距,還是完全采用即時(shí)編譯器執(zhí)行甲锡。如下所示:
??-Xint:完全采用解釋器模式執(zhí)行程序;
??-Xcomp:完全采用即時(shí)編譯器模式執(zhí)行程序。如果即時(shí)編譯出現(xiàn)問題羽戒,解釋器會(huì)介入執(zhí)行缤沦。
??-Xmixed:采用解釋器+即時(shí)編譯器的混合模式共同執(zhí)行程序。
Hotspot VM中JIT的分類
在HotSpot VM中內(nèi)嵌有兩個(gè)JIT編譯器易稠,分別為Client Compiler和Server Compiler缸废,但大多數(shù)情況下我們簡(jiǎn)稱為C1編譯器和C2編譯器。開發(fā)人員可以通過如下命令顯式指定Java虛擬機(jī)在運(yùn)行時(shí)到底使用哪一種即時(shí)編譯器驶社,如下所示:
●?-client:指定Java虛擬機(jī)運(yùn)行在client模式下企量,并使用C1編譯器;
???C1編譯器會(huì)對(duì)字節(jié)碼進(jìn)行簡(jiǎn)單和可靠的優(yōu)化,耗時(shí)短亡电。以達(dá)到更快的編譯速度届巩。
●?-server:指定Java虛擬機(jī)運(yùn)行在Server模式下,并使用C2編譯器份乒。
???C2進(jìn)行耗時(shí)較長(zhǎng)的優(yōu)化恕汇,以及激進(jìn)優(yōu)化。但優(yōu)化的代碼執(zhí)行效率更高或辖。
分層編譯:
分層編譯(Tiered Compilation)策略:程序解釋執(zhí)行(不開啟性能監(jiān)控)可以觸發(fā)C1編譯拇勃,將字節(jié)碼編譯成機(jī)器碼,可以進(jìn)行簡(jiǎn)單優(yōu)化孝凌,也可以加上性能監(jiān)控方咆,C2編譯會(huì)根據(jù)性能監(jiān)控信息進(jìn)行激進(jìn)優(yōu)化。
不過在Java7版本之后蟀架,一旦開發(fā)人員在程序中顯式指定命令“-server"時(shí)瓣赂,默認(rèn)將會(huì)開啟分層編譯策略,由C1編譯器和C2編譯器相互協(xié)作共同來執(zhí)行編譯任務(wù)片拍。
C1和C2編譯器不同的優(yōu)化策略:
●? 在不同的編譯器上有不同的優(yōu)化策略煌集,C1編譯器上主要有方法內(nèi)聯(lián),去虛擬化捌省、冗余消除苫纤。
???方法內(nèi)聯(lián):將引用的函數(shù)代碼編譯到引用點(diǎn)處,這樣可以減少棧幀的生成,減少參數(shù)傳遞以及跳轉(zhuǎn)過程
???去虛擬化:對(duì)唯一的實(shí)現(xiàn)類進(jìn)行內(nèi)聯(lián)
???冗余消除:在運(yùn)行期間把一些不會(huì)執(zhí)行的代碼折疊掉
●?C2的優(yōu)化主要是在全局層面卷拘,逃逸分析是優(yōu)化的基礎(chǔ)喊废。基于逃逸分析在C2上有如下幾種優(yōu)化:
???標(biāo)量替換:用標(biāo)量值代替聚合對(duì)象的屬性值
???棧上分配:對(duì)于未逃逸的對(duì)象分配對(duì)象在棧而不是堆
???同步消除:清除同步操作栗弟,通常指synchronized
總結(jié)
●?一般來講污筷,JIT編譯出來的機(jī)器碼性能比解釋器高。
●?C2編譯器啟動(dòng)時(shí)長(zhǎng)比C1編譯器慢乍赫,系統(tǒng)穩(wěn)定執(zhí)行以后瓣蛀,C2編譯器執(zhí)行速度遠(yuǎn)遠(yuǎn)快于C1編譯器。
●?自JDK10起雷厂,HotSpot又加入一個(gè)全新的即時(shí)編譯器:Graal編譯器惋增。
●?編譯效果短短幾年時(shí)間就追評(píng)了C2編譯器。未來可期
●?目前改鲫,帶著“實(shí)驗(yàn)狀態(tài)"標(biāo)簽器腋,需要使用開關(guān)參數(shù)-XX: +UnlockExperimentalVMOptions -XX: +UseJVMCICompiler去激活,才可以使用钩杰。
●?jdk9引入了AOT編譯器(靜態(tài)提前編譯器纫塌,Ahead Of Time Compiler)
●?Java 9引入了實(shí)驗(yàn)性AOT編譯工具jaotc。它借助了Graal 編譯器讲弄,將所輸入的Java 類文件轉(zhuǎn)換為機(jī)器碼措左,并存放至生成的動(dòng)態(tài)共享庫(kù)之中。
●?所謂AOT編譯避除,是與即時(shí)編譯相對(duì)立的一個(gè) 概念怎披。我們知道,即時(shí)編譯指的是在程序的運(yùn)行過程中瓶摆,將字節(jié)碼轉(zhuǎn)換為可在硬件上直接運(yùn)行的機(jī)器碼凉逛,并部署至托管環(huán)境中的過程。而AOT編譯指的則是群井,在程序運(yùn)行之前状飞,便將字節(jié)碼轉(zhuǎn)換為機(jī)器碼的過程。
●?最大好處: Java虛擬機(jī)加載已經(jīng)預(yù)編譯成二進(jìn)制庫(kù)书斜,可以直接執(zhí)行诬辈。不必等待即時(shí)編譯器的預(yù)熱,減少Java應(yīng)用給人帶來“第一次運(yùn)行慢”的不良體驗(yàn)荐吉。
●?缺點(diǎn):
???破壞了java“一次編譯焙糟,到處運(yùn)行”,必須為每個(gè)不同硬件样屠、OS編譯對(duì)應(yīng)的發(fā)行包穿撮。
???降低了Java鏈接過程的動(dòng)態(tài)性缺脉,加載的代碼在編譯期就必須全部已知。
???還需要繼續(xù)優(yōu)化中悦穿,最初只支持Linux x64 java base