淺談對JIT編譯器的理解

原文:https://insistence.cnblogs.com/p/5901457.html

1. 什么是Just In Time編譯器?

Hot Spot 編譯

當(dāng) JVM 執(zhí)行代碼時,它并不立即開始編譯代碼姐呐。這主要有兩個原因:

首先,如果這段代碼本身在將來只會被執(zhí)行一次系枪,那么從本質(zhì)上看雀哨,編譯就是在浪費(fèi)精力磕谅。因?yàn)閷⒋a翻譯成 java 字節(jié)碼相對于編譯這段代碼并執(zhí)行代碼來說,要快很多雾棺。

當(dāng) 然膊夹,如果一段代碼頻繁的調(diào)用方法,或是一個循環(huán)捌浩,也就是這段代碼被多次執(zhí)行放刨,那么編譯就非常值得了。因此尸饺,編譯器具有的這種權(quán)衡能力會首先執(zhí)行解釋后的代 碼进统,然后再去分辨哪些方法會被頻繁調(diào)用來保證其本身的編譯。其實(shí)說簡單點(diǎn)浪听,就是 JIT 在起作用螟碎,我們知道,對于 Java 代碼迹栓,剛開始都是被編譯器編譯成字節(jié)碼文件掉分,然后字節(jié)碼文件會被交由 JVM 解釋執(zhí)行,所以可以說 Java 本身是一種半編譯半解釋執(zhí)行的語言。Hot Spot VM 采用了 JIT compile 技術(shù)酥郭,將運(yùn)行頻率很高的字節(jié)碼直接編譯為機(jī)器指令執(zhí)行以提高性能华坦,所以當(dāng)字節(jié)碼被 JIT 編譯為機(jī)器碼的時候,要說它是編譯執(zhí)行的也可以不从。也就是說惜姐,運(yùn)行時,部分代碼可能由 JIT 翻譯為目標(biāo)機(jī)器指令(以 method 為翻譯單位椿息,還會保存起來载弄,第二次執(zhí)行就不用翻譯了)直接執(zhí)行。

第二個原因是最優(yōu)化撵颊,當(dāng) JVM 執(zhí)行某一方法或遍歷循環(huán)的次數(shù)越多宇攻,就會更加了解代碼結(jié)構(gòu),那么 JVM 在編譯代碼的時候就做出相應(yīng)的優(yōu)化倡勇。

我 們將在后面講解這些優(yōu)化策略逞刷,這里,先舉一個簡單的例子:我們知道 equals() 這個方法存在于每一個 Java Object 中(因?yàn)槭菑?Object class 繼承而來)而且經(jīng)常被覆寫妻熊。當(dāng)解釋器遇到 b = obj1.equals(obj2) 這樣一句代碼夸浅,它則會查詢 obj1 的類型從而得知到底運(yùn)行哪一個 equals() 方法。而這個動態(tài)查詢的過程從某種程度上說是很耗時的扔役。

在主流商用JVM(HotSpot帆喇、J9)中,Java程序一開始是通過解釋器(Interpreter)進(jìn)行解釋執(zhí)行的亿胸。當(dāng)JVM發(fā)現(xiàn)某個方法或代碼塊運(yùn)行特別頻繁時坯钦,就會把這些代碼認(rèn)定為“熱點(diǎn)代碼(Hot Spot Code)”,然后JVM會把這些代碼編譯成與本地平臺相關(guān)的機(jī)器碼侈玄,并進(jìn)行各種層次的優(yōu)化婉刀,完成這個任務(wù)的編譯器稱為:即時編譯器(Just In Time Compiler,JIT)

JIT編譯器是“動態(tài)編譯器”的一種序仙,相對的“靜態(tài)編譯器”則是指的比如:C/C++的編譯器

JIT并不是JVM的必須部分突颊,JVM規(guī)范并沒有規(guī)定JIT必須存在,更沒有限定和指導(dǎo)JIT潘悼。但是律秃,JIT性能的好壞、代碼優(yōu)化程度的高低卻是衡量一款JVM是否優(yōu)秀的最關(guān)鍵指標(biāo)之一治唤,也是虛擬機(jī)中最核心且最能體現(xiàn)虛擬機(jī)技術(shù)水平的部分棒动。


2. 編譯器與解釋器

首先,不是所有JVM都采用編譯器和解釋器并存的架構(gòu)肝劲,但主流商用虛擬機(jī)迁客,都同時包含這兩部分郭宝。

2.1 配合過程

  1. 當(dāng)程序需要迅速啟動然后執(zhí)行的時候,解釋器可以首先發(fā)揮作用掷漱,編譯器不運(yùn)行從而省去編譯時間粘室,立即執(zhí)行程序

  2. 在程序運(yùn)行后,隨著時間的推移卜范,編譯器逐漸發(fā)揮作用衔统,把越來越多的代碼編譯成本地代碼之后,可以獲得更高的執(zhí)行效率

  3. 當(dāng)程序運(yùn)行環(huán)境中內(nèi)存資源限制較大(如部分嵌入式系統(tǒng)中)海雪,可以使用解釋執(zhí)行來節(jié)約內(nèi)存锦爵;反之,則可以使用編譯執(zhí)行來提升效率奥裸。

  4. 同時险掀,解釋器還可以作為編譯器(C2才會激進(jìn)優(yōu)化)激進(jìn)優(yōu)化時的一個“逃生門”,讓編譯器根據(jù)概率選擇一些大多數(shù)時候都能提升運(yùn)行速度的優(yōu)化手段湾宙,當(dāng)激進(jìn)優(yōu)化假設(shè)不成立樟氢。如:加載了新類后,類型繼承結(jié)構(gòu)出現(xiàn)變化侠鳄,出現(xiàn)“罕見陷阱(Uncommon Trap)”時埠啃,可以通過逆優(yōu)化(Deoptimization)退回到解釋狀態(tài)繼續(xù)執(zhí)行
    (部分沒有解釋器的虛擬機(jī),也會采用不進(jìn)行激進(jìn)優(yōu)化的C1編譯器擔(dān)任“逃生門”的角色)

    這里寫圖片描述

2.2 解釋器 - Interpreter

Interpreter解釋執(zhí)行class文件伟恶,好像JavaScript執(zhí)行引擎一樣

特殊的例子:

  • 最早的Sun Classic VM只有Interpreter
  • BEA JRockit VM則只有Compiler碴开,但它主要面向服務(wù)端應(yīng)用,部署在其上的應(yīng)用不重點(diǎn)關(guān)注啟動時間

2.3 編譯器 - Compiler

只說HotSpot JVM

1. C1和C2:

HotSpot虛擬機(jī)內(nèi)置了兩個即時編譯器博秫,分別稱為Client Compiler和Server Compiler潦牛,習(xí)慣上將前者稱為C1,后者稱為C2

2. 使用C1還是C2台盯?

HotSpot默認(rèn)采用解釋器和其中一個編譯器直接配合的方式工作罢绽,使用那個編譯器取決于虛擬機(jī)運(yùn)行的模式,HotSpot會根據(jù)自身版本和宿主機(jī)器硬件性能自動選擇模式静盅,用戶也可以使用“-client”或”-server”參數(shù)去指定

  1. 混合模式(Mixed Mode)
    默認(rèn)的模式,如上面描述的這種方式就是mixed mode

  2. 解釋模式(Interpreted Mode)
    可以使用參數(shù)“-Xint”寝殴,在此模式下全部代碼解釋執(zhí)行

  3. 編譯模式(Compiled Mode)
    參數(shù)“-Xcomp”蒿叠,此模式優(yōu)先采用編譯,但是無法編譯時也會解釋(在最新的HotSpot中此參數(shù)被取消)

    可以看到蚣常,我的JVM現(xiàn)在是mixed mode


    這里寫圖片描述

重要:↓

在JDK1.7(1.7僅包括Server模式)之后市咽,HotSpot就不是默認(rèn)“采用解釋器和其中一個編譯器”配合的方式了,而是采用了分層編譯抵蚊,分層編譯時C1和C2有可能同時工作


3. 分層編譯

3.1 為什么要分層編譯施绎?

由于編譯器compile本地代碼需要占用程序時間溯革,要編譯出優(yōu)化程度更高的代碼所花費(fèi)的時間可能更長,且此時解釋器還要替編譯器收集性能監(jiān)控信息谷醉,這對解釋執(zhí)行的速度也有影響

所以致稀,為了在程序啟動響應(yīng)時間與運(yùn)行效率之間達(dá)到最佳平衡,HotSpot在JDK1.6中出現(xiàn)了分層編譯(Tiered Compilation)的概念并在JDK1.7的Server模式JVM中作為默認(rèn)策略被開啟

3.2 編譯層 tier(或者叫級別)

分層編譯根據(jù)編譯器編譯俱尼、優(yōu)化的規(guī)模與耗時抖单,劃分了不同的編譯層次(不只以下3種),包括:

  • 第0層遇八,程序解釋執(zhí)行(沒有編譯)矛绘,解釋器不開啟性能監(jiān)控功能,可觸發(fā)第1層編譯刃永。

  • 第1層货矮,也稱C1編譯,將字節(jié)碼編譯為本地代碼斯够,進(jìn)行簡單次屠、可靠的優(yōu)化,如有必要將加入性能監(jiān)控的邏輯

  • 第2層(或2層以上)雳刺,也稱為C2編譯劫灶,也是將字節(jié)碼編譯為本地代碼,但是會啟用一些編譯耗時較長的優(yōu)化掖桦,甚至?xí)?/strong>根據(jù)性能監(jiān)控信息進(jìn)行一些不可靠的激進(jìn)優(yōu)化

實(shí)施分層編譯后本昏,C1和C2將會同時工作,許多代碼會被多次編譯枪汪,用C1獲取更高的編譯速度涌穆,用C2來獲取更好的編譯質(zhì)量,且在解釋執(zhí)行的時候解釋器也無須再承擔(dān)收集性能監(jiān)控信息的任務(wù)


4. 編譯對象與觸發(fā)條件

1. 誰被編譯了雀久?

編譯對象就是之前說的“熱點(diǎn)代碼”宿稀,它有兩類:

  1. 被多次調(diào)用的方法

    • 一個方法被多次調(diào)用,理應(yīng)稱為熱點(diǎn)代碼赖捌,這種編譯也是虛擬機(jī)中標(biāo)準(zhǔn)的JIT編譯方式
  2. 被多次執(zhí)行的循環(huán)體

    • 編譯動作由循環(huán)體出發(fā)祝沸,但編譯對象依然會以整個方法為對象
    • 這種編譯方式由于編譯發(fā)生在方法執(zhí)行過程中越庇,因此形象的稱為:棧上替換(On Stack Replacement- OSR編譯罩锐,即方法棧幀還在棧上,方法就被替換了)

2. 觸發(fā)條件

1. 綜述

上面的方法和循環(huán)體都說“多次”卤唉,那么多少算多涩惑?換個說法就是編譯的觸發(fā)條件。

判斷一段代碼是不是熱點(diǎn)代碼桑驱,是不是需要觸發(fā)JIT編譯竭恬,這樣的行為稱為:熱點(diǎn)探測(Hot Spot Detection)跛蛋,有幾種主流的探測方式:

  1. 基于計(jì)數(shù)器的熱點(diǎn)探測(Counter Based Hot Spot Detection)
    虛擬機(jī)會為每個方法(或每個代碼塊)建立計(jì)數(shù)器,統(tǒng)計(jì)執(zhí)行次數(shù)痊硕,如果超過閥值那么就是熱點(diǎn)代碼赊级。缺點(diǎn)是維護(hù)計(jì)數(shù)器開銷。

  2. 基于采樣的熱點(diǎn)探測(Sample Based Hot Spot Detection)
    虛擬機(jī)會周期性檢查各個線程的棧頂寿桨,如果某個方法經(jīng)常出現(xiàn)在棧頂此衅,那么就是熱點(diǎn)代碼。缺點(diǎn)是不精確亭螟。

  3. 基于蹤跡的熱點(diǎn)探測(Trace Based Hot Spot Detection)
    Dalvik中的JIT編譯器使用這種方式

2. HotSpot

HotSpot使用的是第1種挡鞍,因此它為每個方法準(zhǔn)備了兩類計(jì)數(shù)器:方法調(diào)用計(jì)數(shù)器(Invocation Counter)和回邊計(jì)數(shù)器(Back Edge Counter)

  1. 方法計(jì)數(shù)器

    • 默認(rèn)閥值,在Client模式下是1500次预烙,Server是10000次墨微,可以通過參數(shù)“-XX:CompileThreshold”來設(shè)定

    • 當(dāng)一個方法被調(diào)用時會首先檢查是否存在被JIT編譯過得版本,如果存在則使用此本地代碼來執(zhí)行扁掸;如果不存在翘县,則將方法計(jì)數(shù)器+1,然后判斷“方法計(jì)數(shù)器和回邊計(jì)數(shù)器之和”是否超過閥值谴分,如果是則會向編譯器提交一個方法編譯請求

    • 默認(rèn)情況下锈麸,執(zhí)行引擎并不會同步等待上面的編譯完成,而是會繼續(xù)解釋執(zhí)行牺蹄。當(dāng)編譯完成后忘伞,此方法的調(diào)用入口地址會被系統(tǒng)自動改寫為新的本地代碼地址

    • 還有一點(diǎn)彰阴,熱度是會衰減的顾腊,也就是說不是僅僅+验毡,也會-逃延,熱度衰減動作是在虛擬機(jī)的GC執(zhí)行時順便進(jìn)行的

  2. 回邊計(jì)數(shù)器

    • 回邊,顧名思義贺氓,只有執(zhí)行到大括號”}”時才算+1

    • 默認(rèn)閥值蹲缠,Client下13995仗扬,Server下10700

    • 它的調(diào)用邏輯和方法計(jì)數(shù)器差不多斋射,只不過遇到回邊指令時+1育勺、超過閥值時會提交OSR編譯請求以及這里沒有熱度衰減


5. 編譯過程

編譯過程是在后臺線程(daemon)中完成的,可以通過參數(shù)“-XX:-BackgroundCompilation”來禁止后臺編譯绩鸣,但此時執(zhí)行線程就會同步等待編譯完成才會執(zhí)行程序

  1. Client Compiler
    C1編譯器是一個簡單快速的三段式編譯器怀大,主要關(guān)注“局部性能優(yōu)化”,放棄許多耗時較長的全局優(yōu)化手段
    過程:class -> 1. 高級中間代碼 -> 2. 低級中間代碼 -> 3. 機(jī)器代碼
  2. Server Compiler
    C2是專門面向服務(wù)器應(yīng)用的編譯器呀闻,是一個充分優(yōu)化過的高級編譯器,幾乎能達(dá)到GNU C++編譯器使用-O2參數(shù)時的優(yōu)化強(qiáng)度潜慎。

使用參數(shù)“-XX:+PrintCompilation”會讓虛擬機(jī)在JIT時把方法名稱打印出來捡多,如圖:


這里寫圖片描述

6. Java和C/C++的編譯器對比

這里不是比Java和C/C++誰快這種大坑問題蓖康,只是比較編譯器(我認(rèn)為開發(fā)效率上Java快,執(zhí)行效率上C/C++快)

這種對比代表了經(jīng)典的即時編譯器與靜態(tài)編譯期的對比垒手,其實(shí)總體來說Java編譯器有優(yōu)有劣蒜焊。主要就是動態(tài)編譯時間壓力大能做的優(yōu)化少,還要做一些動態(tài)校驗(yàn)科贬。而靜態(tài)編譯器無法實(shí)現(xiàn)一些開發(fā)上很有用的動態(tài)特性

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泳梆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子榜掌,更是在濱河造成了極大的恐慌优妙,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件憎账,死亡現(xiàn)場離奇詭異套硼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)胞皱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門邪意,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人反砌,你說我怎么就攤上這事雾鬼。” “怎么了宴树?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵策菜,是天一觀的道長。 經(jīng)常有香客問我森渐,道長做入,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任同衣,我火速辦了婚禮竟块,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘耐齐。我一直安慰自己浪秘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布埠况。 她就那樣靜靜地躺著耸携,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辕翰。 梳的紋絲不亂的頭發(fā)上夺衍,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機(jī)與錄音喜命,去河邊找鬼沟沙。 笑死河劝,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的矛紫。 我是一名探鬼主播赎瞎,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼颊咬!你這毒婦竟也來了务甥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤喳篇,失蹤者是張志新(化名)和其女友劉穎敞临,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杭隙,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哟绊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了痰憎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片票髓。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖铣耘,靈堂內(nèi)的尸體忽然破棺而出洽沟,到底是詐尸還是另有隱情,我是刑警寧澤蜗细,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布裆操,位于F島的核電站,受9級特大地震影響炉媒,放射性物質(zhì)發(fā)生泄漏踪区。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一吊骤、第九天 我趴在偏房一處隱蔽的房頂上張望缎岗。 院中可真熱鬧,春花似錦白粉、人聲如沸传泊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽眷细。三九已至,卻和暖如春鹃祖,著一層夾襖步出監(jiān)牢的瞬間溪椎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留池磁,地道東北人奔害。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓楷兽,卻偏偏與公主長得像地熄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子芯杀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理端考,因此不免有一些不準(zhǔn)確的地方,同時不同JDK版本的...
    高廣超閱讀 15,608評論 3 83
  • 《深入理解Java虛擬機(jī)》筆記_第一遍 先取看完這本書(JVM)后必須掌握的部分揭厚。 第一部分 走近 Java 從傳...
    xiaogmail閱讀 5,097評論 1 34
  • java編譯器却特,java解釋器 1.java程序是一種可跨平臺執(zhí)行的語言,之所以可以跨平臺筛圆,是因?yàn)閖vm的存在裂明,J...
    rabbit_coding閱讀 6,998評論 2 18
  • 自信,每個人都是有的太援,只是在不同時間段以及不同行事上表現(xiàn)不同闽晦,否則活著都是問題,都是悲苦的提岔。 自信是個內(nèi)心的問題仙蛉,...
    山賊爺閱讀 283評論 0 0
  • 曲:劉子菲(織女心絲) 詞:深海的記憶 風(fēng)煙卷 悠悠南山 霧縹散 東籬邊 戴月歸返 誰歌弦 遠(yuǎn)刀劍 止戰(zhàn)亂 雞鳴破...
    向史而新閱讀 178評論 0 3