在 HotSpot 里面,上述翻譯過程有兩種形式:
第一種是解釋執(zhí)行,即逐條將字節(jié)碼翻譯成機(jī)器碼并執(zhí)行;
第二種是即時(shí)編譯(Just-In-Time compilation,JIT),即將一個(gè)方法中包含的所有字節(jié)碼編譯成機(jī)器碼后再執(zhí)行。
前者的優(yōu)勢(shì)在于無需等待編譯,而后者的優(yōu)勢(shì)在于實(shí)際運(yùn)行速度更快。HotSpot 默認(rèn)采用混合模式,綜合了解釋執(zhí)行和即時(shí)編譯兩者的優(yōu)點(diǎn)。它會(huì)先解釋執(zhí)行字節(jié)碼,而后將其中反復(fù)執(zhí)行的熱點(diǎn)代碼,以方法為單位進(jìn)行即時(shí)編譯。
HotSpot 采用了多種技術(shù)來提升啟動(dòng)性能以及峰值性能,剛剛提到的即時(shí)編譯便是其中最重要的技術(shù)之一。
即時(shí)編譯建立在程序符合二八定律的假設(shè)上,也就是百分之二十的代碼占據(jù)了百分之八十的計(jì)算資源。
對(duì)于占據(jù)大部分的不常用的代碼,我們無需耗費(fèi)時(shí)間將其編譯成機(jī)器碼,而是采取解釋執(zhí)行的方式運(yùn)行;另一方面,對(duì)于僅占據(jù)小部分的熱點(diǎn)代碼,我們則可以將其編譯成機(jī)器碼,以達(dá)到理想的運(yùn)行速度。
理論上講,即時(shí)編譯后的 Java 程序的執(zhí)行效率,是可能超過 C++ 程序的。這是因?yàn)榕c靜態(tài)編譯相比,即時(shí)編譯擁有程序的運(yùn)行時(shí)信息,并且能夠根據(jù)這個(gè)信息做出相應(yīng)的優(yōu)化。
舉個(gè)例子,我們知道虛方法是用來實(shí)現(xiàn)面向?qū)ο笳Z言多態(tài)性的。對(duì)于一個(gè)虛方法調(diào)用,盡管它有很多個(gè)目標(biāo)方法,但在實(shí)際運(yùn)行過程中它可能只調(diào)用其中的一個(gè)。這個(gè)信息便可以被即時(shí)編譯器所利用,來規(guī)避虛方法調(diào)用的開銷,從而達(dá)到比靜態(tài)編譯的 C++ 程序更高的性能。
為了滿足不同用戶場(chǎng)景的需要,HotSpot 內(nèi)置了多個(gè)即時(shí)編譯器:C1、C2 和 Graal。
Graal 是 Java 10 正式引入的實(shí)驗(yàn)性即時(shí)編譯器,在專欄的第四部分我會(huì)詳細(xì)介紹,這里暫不做討論。之所以引入多個(gè)即時(shí)編譯器,是為了在編譯時(shí)間和生成代碼的執(zhí)行效率之間進(jìn)行取舍。
C1 又叫做 Client 編譯器,面向的是對(duì)啟動(dòng)性能有要求的客戶端 GUI 程序,采用的優(yōu)化手段相對(duì)簡(jiǎn)單,因此編譯時(shí)間較短。
C2 又叫做 Server 編譯器,面向的是對(duì)峰值性能有要求的服務(wù)器端程序,采用的優(yōu)化手段相對(duì)復(fù)雜,因此編譯時(shí)間較長(zhǎng),但同時(shí)生成代碼的執(zhí)行效率較高。
從 Java 7 開始,HotSpot 默認(rèn)采用分層編譯的方式:熱點(diǎn)方法首先會(huì)被 C1 編譯,而后熱點(diǎn)方法中的熱點(diǎn)會(huì)進(jìn)一步被 C2 編譯。
為了不干擾應(yīng)用的正常運(yùn)行,HotSpot 的即時(shí)編譯是放在額外的編譯線程中進(jìn)行的。HotSpot 會(huì)根據(jù) CPU 的數(shù)量設(shè)置編譯線程的數(shù)目,并且按 1:2 的比例配置給 C1 及 C2 編譯器。
在計(jì)算資源充足的情況下,字節(jié)碼的解釋執(zhí)行和即時(shí)編譯可同時(shí)進(jìn)行。編譯完成后的機(jī)器碼會(huì)在下次調(diào)用該方法時(shí)啟用,以替換原本的解釋執(zhí)行。
更多關(guān)于“Java培訓(xùn)”的問題,歡迎咨詢千鋒教育在線名師。千鋒已有十余年的培訓(xùn)經(jīng)驗(yàn),課程大綱更科學(xué)更專業(yè),有針對(duì)零基礎(chǔ)的就業(yè)班,有針對(duì)想提升技術(shù)的好程序員班,高品質(zhì)課程助力你實(shí)現(xiàn)java程序員夢(mèng)想。