<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          聊一聊 JIT 即時(shí)編譯

          共 2356字,需瀏覽 5分鐘

           ·

          2021-02-28 22:30

          良心公眾號(hào)

          關(guān)注不迷路


          JIT 編譯器,全稱 Just In Time Compiler,被稱作即時(shí)編譯器。



          01

          JIT 的應(yīng)用背景


          只看定義,并不能很清楚地了解 JIT 編譯器的真實(shí)面目。這一切還要從 Java 語言的自身特點(diǎn)說起。


          Java 語言有一個(gè)重要的特性,“一次編譯,到處運(yùn)行”。該特性是依賴于“字節(jié)碼”這樣一種中間形式來實(shí)現(xiàn)的。具體來說,要想運(yùn)行一段 Java 程序,首先需要利用 javac 將程序編譯成字節(jié)碼,但由于計(jì)算機(jī)并不認(rèn)識(shí)字節(jié)碼,只認(rèn)識(shí)機(jī)器碼,因此,還需要一個(gè)被稱為“解釋器”的翻譯官,將字節(jié)碼逐條解釋為機(jī)器碼,從而使代碼最終得以執(zhí)行。


          按照上面的描述,Java 程序要想被計(jì)算機(jī)成功執(zhí)行,就必須先經(jīng)過編譯和解釋這兩個(gè)步驟,因此,Java 又被稱為“解釋型”語言。而對(duì)于 C++ 這種語言來說,C++ 程序在執(zhí)行之前會(huì)直接被編譯器編譯成機(jī)器碼,而沒有字節(jié)碼這樣的中間狀態(tài),也就沒有了解釋執(zhí)行這一步驟。因此,曾經(jīng)流行這樣一種說法,C++ 的執(zhí)行效率要高于 Java。不過,也正因?yàn)闆]有字節(jié)碼這樣的中間狀態(tài),C++ 也很難像 Java 一樣做到“一次編譯,到處運(yùn)行”。


          聽起來有一種“魚與熊掌不可兼得”的意味,但其實(shí)并非如此,或者說,這其間還是有優(yōu)化空間的。而 JVM 在這方面的探索,就是引入了本文要講述的 JIT 編譯器。



          02

          JIT 的基本思路


          JIT 編譯器的基本思路是這樣的,既然解釋執(zhí)行太慢,那就將字節(jié)碼進(jìn)一步編譯成適用于本地計(jì)算機(jī)的機(jī)器碼。按照這種思路,就可以既保證平臺(tái)無關(guān)性,又能進(jìn)一步提升效率。進(jìn)一步考慮,將字節(jié)碼編譯成機(jī)器碼也是需要時(shí)間的,因此,如果某些指令執(zhí)行的頻率相當(dāng)?shù)停藭r(shí)編譯成機(jī)器碼對(duì)系統(tǒng)效率的提升可以說是收效甚微,甚至是負(fù)收益,此時(shí)就可以繼續(xù)保持解釋執(zhí)行的方式,而對(duì)于那些執(zhí)行頻率較高的指令,編譯成機(jī)器碼的收益自然就十分明顯了。


          可以用下圖來大致描述 JVM 中引入 JIT 的基本思路。

          610e942a18ddca85af0d735eb55e51e1.webp

          圖中紅色方塊中的編譯,其實(shí)就是文章開頭提到的 JIT 編譯器在發(fā)揮作用。



          03

          兩大類 JIT 編譯器


          進(jìn)一步來看,JVM 集成了兩類編譯器,分別稱之為?Client CompilerServer Compiler。它們的主要特點(diǎn)如下:

          • Client Compiler 注重啟動(dòng)速度局部的可靠的優(yōu)化。該類編譯器是 GUI 應(yīng)用程序的理想選擇。

          • Server Compiler 注重全局優(yōu)化,優(yōu)化方式更加激進(jìn),因此,在一般情況下,它的整體性能會(huì)更好,但不可避免地是,全局優(yōu)化需要進(jìn)行更多的全局分析,這會(huì)導(dǎo)致啟動(dòng)速度變慢。該類編譯器是長(zhǎng)期運(yùn)行的服務(wù)器端應(yīng)用程序的理想選擇。

          可以看到,兩類編譯器有著不同的應(yīng)用場(chǎng)景,在虛擬機(jī)中共同發(fā)揮作用。


          以?HotSpot 虛擬機(jī)為例,它的 Client Compiler 被稱作 C1 編譯器,而?Server Compiler?主要有兩種:C2 編譯器和自 JDK10?開始引入的 Graal 編譯器 (實(shí)驗(yàn)性)。


          C1 編譯器

          C1 編譯器主要做以下工作:

          • 對(duì)字節(jié)碼進(jìn)行基礎(chǔ)優(yōu)化,包括方法內(nèi)聯(lián)、常量傳播等。

          • 將字節(jié)碼構(gòu)造成一種與目標(biāo)機(jī)器指令集無關(guān)的高級(jí)中間代碼表示?(HIR),并對(duì)其進(jìn)行一些優(yōu)化,包括空值檢查消除、范圍檢查消除等。

          • 從 HIR 中產(chǎn)生與目標(biāo)機(jī)器指令集相關(guān)的低級(jí)中間代碼表示 (LIR),并在 LIR 的基礎(chǔ)上進(jìn)行寄存器分配、窺孔優(yōu)化等操作,最終生成機(jī)器碼。


          對(duì)于 C1 編譯器的大致工作流程可以用下圖進(jìn)行表示:

          182f90dc19fffb3cd0d717849fccef2e.webp


          C2 編譯器

          C2 編譯器主要做以下工作:

          • 對(duì)字節(jié)碼進(jìn)行解析,構(gòu)建一種被稱作 Ideal Graph 的圖數(shù)據(jù)結(jié)構(gòu),用來表示程序的數(shù)據(jù)流向和指令之間的依賴關(guān)系。在解析字節(jié)碼的過程中,JVM 會(huì)進(jìn)行指令優(yōu)化,包括?Global Value Numbering、常量折疊等,解析成功之后,會(huì)做一些包括死代碼剔除等在內(nèi)的優(yōu)化工作。

          • JVM 判斷是否需要進(jìn)行全局優(yōu)化,如果需要?jiǎng)t進(jìn)行全局優(yōu)化,否則跳過。

          • 生成 MachNode Graph,進(jìn)行寄存器分配、窺孔優(yōu)化等操作,并最終生成機(jī)器碼。


          對(duì)于 C2 編譯器的大致工作流程可以用下圖進(jìn)行表示:

          41df93a4fc8acecddd6e5f8d2e66bcf4.webp

          Graal 編譯器

          Graal 編譯器是自 JDK10?開始引入的一種較新的 Server Compiler,目前仍處于實(shí)驗(yàn)階段。同 C2 相比,它主要有以下特點(diǎn):

          • 對(duì) JDK8 以來支持的 Lambda 和 Stream 等新特性更加友好。

          • 優(yōu)化更加激進(jìn),峰值性能更高。

          • 支持逃逸分析等更加深層次的優(yōu)化。



          04

          分層編譯


          不僅如此,JDK7 引入了分層編譯的概念,綜合了 C1 的啟動(dòng)性能優(yōu)勢(shì)和 C2 的峰值性能優(yōu)勢(shì)。


          分層編譯將 JVM 的執(zhí)行狀態(tài)分為了五個(gè)層次。分別為:

          • 解釋執(zhí)行;

          • 執(zhí)行不帶 profiling 的 C1 代碼;

          • 執(zhí)行僅帶方法調(diào)用次數(shù)以及循環(huán)回邊執(zhí)行次數(shù) profiling 的 C1 代碼;

          • 執(zhí)行帶所有 profiling 的 C1 代碼;

          • 執(zhí)行 C2 代碼


          如下圖所示:

          ac1929cfcbfc0119616139f003146410.webp

          通常情況下,C2 代碼的執(zhí)行效率要比 C1 代碼的高 30% 以上。而對(duì)于 C1 代碼的三層來說,執(zhí)行效率是遞減的。這是因?yàn)?profiling 越多,額外的性能開銷越大。


          另外,可以看到,第 1 層和第 4 層處于終止?fàn)顟B(tài)當(dāng)一個(gè)方法被終止?fàn)顟B(tài)編譯過后,在編譯后的代碼沒有失效的前提下,JVM 不會(huì)再次發(fā)出該方法的編譯請(qǐng)求。


          本文關(guān)于 JIT時(shí)編譯的基本知識(shí)總結(jié)就到這里了,文章部分內(nèi)容參考自《深入理解Java虛擬機(jī)》其中還有很多優(yōu)化細(xì)節(jié)值得深挖,繼續(xù)探索吧


          歡迎關(guān)注【有理想的菜雞】公眾號(hào),大家一起討論技術(shù),共同成長(zhǎng)!

          af63e35ab5130b57cf6455f40bc532d5.webp

          學(xué)習(xí) | 工作 |?分享

          ??長(zhǎng)按關(guān)注“有理想的菜雞

          只有你想不到,沒有你學(xué)不到
          瀏覽 85
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久日视频在线观看 | 国产插逼视频 | 欧美亚洲日本在线 | 最近中文字幕免费MV第一季歌词怀孕 | 欧美精品成人网站在线 |