日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

深入理解JVM之JIT编译器(二)

發(fā)布時(shí)間:2025/3/21 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解JVM之JIT编译器(二) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

上篇是分析了一下前段編譯器,主要過(guò)程完成從java代碼到字節(jié)碼的轉(zhuǎn)變,它的改進(jìn)頂多是提高程序的編碼速度和效率。本篇嘗試探索JIT編譯器,它能夠完成從字節(jié)碼到本地機(jī)器碼的轉(zhuǎn)變,從而真正的影響程序的運(yùn)行效率。

概念

部分商用虛擬機(jī),程序最初是通過(guò)解釋器(Interpreter)進(jìn)行解釋執(zhí)行,當(dāng)發(fā)現(xiàn)某個(gè)部分代碼頻繁執(zhí)行的時(shí)候,就會(huì)將這些代碼認(rèn)定為「熱點(diǎn)代碼」(即 Hot Spot Code)。為了提高熱點(diǎn)代碼的執(zhí)行效率,在運(yùn)行時(shí),虛擬機(jī)會(huì)把這些代碼編譯成與本地平臺(tái)相關(guān)的機(jī)器碼,并進(jìn)行各種優(yōu)化,完成這個(gè)任務(wù)的編譯器成為即時(shí)編譯器(Just In Time Compiler,簡(jiǎn)稱JIT編譯器)。(下文所指均為JIT)關(guān)于即時(shí)編譯器需要知道的幾點(diǎn):

  • 它并不是VM必需的部分,java虛擬機(jī)規(guī)范并沒有規(guī)定它必須存在,所以也沒限定如何去實(shí)現(xiàn)。
  • 即時(shí)編譯器編譯性能的好壞、代碼優(yōu)化程度高低是衡量商用虛擬機(jī)優(yōu)秀與否最關(guān)鍵指標(biāo)之一。

Hotspot VM的JIT編譯器

解釋器和編譯器

不是所有Java虛擬機(jī)均采用二者并存的架構(gòu),但是一些主流商用虛擬機(jī)如Hotspot、J9都會(huì)同時(shí)包含二者。關(guān)于二者的優(yōu)勢(shì)如下:

  • 程序剛啟動(dòng)和執(zhí)行的時(shí)候,解釋器可以首先發(fā)揮作用,省去編譯的時(shí)間,立即執(zhí)行字節(jié)碼。
  • 程序運(yùn)行之后,經(jīng)過(guò)一段時(shí)間,編譯器可以逐漸發(fā)揮作用,把更多的代碼編譯為本地機(jī)器碼,獲得更高的執(zhí)行效率。
  • 在整個(gè)虛擬機(jī)執(zhí)行架構(gòu)中,解釋器和編譯器經(jīng)常配合工作,如下圖所示:

    上圖中內(nèi)置了兩個(gè)JIT編譯器,分別稱Client Compiler和Server Compiler,簡(jiǎn)稱為C1編譯器和C2編譯器。目前主流的Hotspot VM(jdk1.7及以前版本虛擬機(jī)),默認(rèn)采用與其中一個(gè)編譯器直接配合,程序用哪個(gè),取決于虛擬機(jī)運(yùn)行模式,用戶可以使用-client和-server參數(shù)強(qiáng)制指定虛擬機(jī)運(yùn)行在client和server模式。
    另外,通過(guò)下圖的三種命令可以強(qiáng)制虛擬機(jī)運(yùn)行的模式,分別為:Mixed Mode(混合模式,默認(rèn)情況下解釋器和編譯器搭配使用)、Interpreter Mode(解釋模式)、Compiled Mode(編譯模式)。


    了解完解釋器和編譯器之后,我們想知道那些代碼會(huì)被編譯?在什么情況下會(huì)觸發(fā)編譯?基于這兩個(gè)問(wèn)題,來(lái)了解編譯對(duì)象和觸發(fā)條件。

    編譯對(duì)象

    在運(yùn)行過(guò)程中會(huì)被即時(shí)編譯器編譯的「熱點(diǎn)代碼」有兩類,如下:

    • 被多次調(diào)用的方法
    • 被多次執(zhí)行的循環(huán)體

    關(guān)于這兩種情況,有兩點(diǎn)解釋:對(duì)于第一種情況,是由方法調(diào)用觸發(fā)的編譯,理所當(dāng)然的會(huì)以整個(gè)方法體作為編譯對(duì)象,這種編譯是虛擬機(jī)中標(biāo)準(zhǔn)的JIT編譯。對(duì)于第二種情況,編譯動(dòng)作由循環(huán)體觸發(fā),但是編譯器仍舊會(huì)以整個(gè)方法作為編譯對(duì)象,由于這種方法發(fā)生在方法執(zhí)行過(guò)程中,所以稱為棧上替換(On Stack Replacement,簡(jiǎn)稱OSR編譯,因?yàn)榉椒€在棧上)。

    觸發(fā)條件

    正如上面所說(shuō)的“多次”并不夠嚴(yán)謹(jǐn)和具體,那么如何才算是“多次”呢?以及怎么樣去統(tǒng)計(jì)一個(gè)方法和一段代碼被執(zhí)行多少次呢?回答了這兩個(gè)問(wèn)題就是回答了觸發(fā)條件。判斷一段代碼是不是熱點(diǎn)代碼,是不是需要觸發(fā)即時(shí)編譯,這樣的行為叫「熱點(diǎn)探測(cè)」。關(guān)于熱點(diǎn)探測(cè)有兩種方式,如下:

    • 基于采樣的熱點(diǎn)探測(cè)
      虛擬機(jī)會(huì)周期性檢查各個(gè)線程的棧頂,如果發(fā)現(xiàn)某些方法長(zhǎng)期占據(jù)棧頂,那么會(huì)被認(rèn)為是「熱點(diǎn)代碼」。簡(jiǎn)單、高效,但是不夠精確,因?yàn)槟承┓椒〞?huì)因?yàn)榫€程阻塞或其他原因擾亂熱點(diǎn)探測(cè)。
    • 基于計(jì)數(shù)器的熱點(diǎn)探測(cè)(Hotspot VM采用)
      虛擬機(jī)為每個(gè)方法或者代碼塊建立計(jì)數(shù)器,統(tǒng)計(jì)執(zhí)行次數(shù),超過(guò)一定閥值會(huì)被認(rèn)為是「熱點(diǎn)代碼」。

    Hotspot VM使用第二種,并且為每個(gè)方法準(zhǔn)備了兩種計(jì)數(shù)器:方法調(diào)用計(jì)數(shù)器和回邊計(jì)數(shù)器(在字節(jié)碼中遇到控制流向后跳轉(zhuǎn)的指令稱為“回邊 ”)。在確定虛擬運(yùn)行參數(shù)的情況下,這兩個(gè)計(jì)數(shù)器都有一個(gè)確定的閥值,超過(guò)這個(gè)閥值就會(huì)觸發(fā)JIT編譯。方法調(diào)用計(jì)數(shù)器觸發(fā)即時(shí)編譯的交互過(guò)程如下圖:

    注意:方法調(diào)用計(jì)數(shù)器統(tǒng)計(jì)的并不是被調(diào)用的絕對(duì)次數(shù),而是一個(gè)相對(duì)的執(zhí)行頻率,即一段時(shí)間被被調(diào)用的次數(shù)。

    編譯過(guò)程

    在默認(rèn)設(shè)置下,無(wú)論是方法調(diào)用產(chǎn)生的即時(shí)編譯請(qǐng)求,還是OSR編譯請(qǐng)求,虛擬機(jī)在代碼編譯還未完成之前,都仍然按照解釋方式執(zhí)行,而編譯動(dòng)作則在后臺(tái)編譯線程中進(jìn)行。至于在后臺(tái)如何執(zhí)行編譯過(guò)程,Client Compiler和Server Compiler的編譯過(guò)程是不同的。二者大致區(qū)別如下:

    • Client Compiler:主要關(guān)注點(diǎn)在局部?jī)?yōu)化,而放棄了耗時(shí)較長(zhǎng)的全局優(yōu)化手段。
    • Server Compiler:是專門面向服務(wù)器端的典型應(yīng)用并為服務(wù)端的性能配置特別調(diào)整過(guò)。

    從即時(shí)編譯的角度看,Server Compiler是比較慢,但其編譯速度又遠(yuǎn)遠(yuǎn)超過(guò)「靜態(tài)優(yōu)化編譯器」,而且比Client Compiler輸出的代碼質(zhì)量高,可以減少本地代碼執(zhí)行時(shí)間,從而抵消額外的編譯時(shí)間。

    編譯優(yōu)化技術(shù)

    之所以有編譯方式執(zhí)行本地代碼比解釋方式更快這樣的共識(shí),原因很簡(jiǎn)單,是因?yàn)樘摂M機(jī)設(shè)計(jì)團(tuán)隊(duì)幾乎把對(duì)代碼所有的優(yōu)化措施集中在了即時(shí)編譯器之中,因此一般來(lái)說(shuō),即時(shí)編譯器產(chǎn)生的本地代碼會(huì)比Javac產(chǎn)生的字節(jié)碼更優(yōu)秀。常用優(yōu)化技術(shù)如下:

    公共子表達(dá)式消除

    語(yǔ)言無(wú)關(guān),比如像:b乘c、c乘b這樣的表達(dá)式值都是一樣的可以直接替換。

    數(shù)組范圍消除

    語(yǔ)言相關(guān),主要思路就是盡可能把運(yùn)行期檢查提任務(wù)前到編譯器進(jìn)行,以至于在循環(huán)遍歷的時(shí)候不需要每次都要判斷變量大小是否超過(guò)數(shù)組范圍,帶來(lái)隱式開銷,只要在編譯期根據(jù)數(shù)據(jù)流獲得數(shù)組的length,并且判斷下標(biāo)沒有越界,執(zhí)行的時(shí)候就無(wú)需判斷了。

    方法內(nèi)聯(lián)

    為了消除方法調(diào)用的成本,同時(shí)為其他優(yōu)化手段建立好的基礎(chǔ)。因?yàn)楹芏喾椒ǚ珠_看是有意義的,如果不做方法內(nèi)聯(lián),即使進(jìn)行了無(wú)用代碼消除,也無(wú)法發(fā)現(xiàn)任何“Dead Code”。

    逃逸分析

    并不是直接的優(yōu)化手段,而是為其他優(yōu)化手段提供分析技術(shù)。逃逸分析的基本行為就是分析對(duì)象的作用域,比如一個(gè)對(duì)象在一個(gè)方法中被定義,可能被外部方法所引用,例如作為方法參數(shù)傳遞到其他方法中,這被稱為方法逃逸。甚至可能被其他線程訪問(wèn),例如賦值給類變量或其他線程訪問(wèn)的實(shí)例變量,就是線程逃逸。

    因此,如果能證明一個(gè)不會(huì)發(fā)生方法或者線程逃逸,則可以為這個(gè)變量進(jìn)行一些高效優(yōu)化。優(yōu)化措施如下:

    • 棧上分配
      將確定不會(huì)逃逸的對(duì)象在在棧幀上進(jìn)行創(chuàng)建分配內(nèi)存,這樣方法結(jié)束時(shí),對(duì)象就會(huì)隨著棧幀出棧而銷毀,減少堆內(nèi)存垃圾回收的壓力。
    • 同步消除
      如果確定一個(gè)變量不會(huì)線程逃逸,也就說(shuō)明該變量不會(huì)發(fā)生線程競(jìng)爭(zhēng),從而消除掉該變量的同步措施。
    • 標(biāo)量替換
      像java中的原始數(shù)據(jù)類型如int、long均稱為標(biāo)量(表示無(wú)法分解為更小的數(shù)據(jù)來(lái)表示了),如果一個(gè)數(shù)據(jù)可以繼續(xù)分解,則為聚合量,而java中的對(duì)象則為典型的聚合量。如果一個(gè)對(duì)象不會(huì)被外部訪問(wèn),而這個(gè)對(duì)象可以被拆散分解,那么就不去創(chuàng)建這個(gè)對(duì)象,而是直接創(chuàng)建被使用到的成員變量。而這些成員變量除了被分配在棧上(棧上的數(shù)據(jù)很容易會(huì)被虛擬機(jī)分配到物理高速寄存器)進(jìn)行讀和寫,還為后續(xù)優(yōu)化創(chuàng)造基礎(chǔ)條件。

    本篇主要了解解釋器和編譯器的特點(diǎn)和各自優(yōu)勢(shì)、然后總結(jié)了編譯對(duì)象和編譯條件,最后介紹了編譯過(guò)程以及用到的一些主要的編譯優(yōu)化技術(shù),算是對(duì)即時(shí)編譯器有個(gè)基本的理解了。

    from:http://zouzls.github.io/2016/09/07/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3JVM%E4%B9%8BJIT%E7%BC%96%E8%AF%91%E5%99%A8%EF%BC%88%E4%BA%8C%EF%BC%89/?

    總結(jié)

    以上是生活随笔為你收集整理的深入理解JVM之JIT编译器(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。