自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 10來了,來看看它一同發(fā)布的全新JIT編譯器

開發(fā) 后端
Java是最廣泛使用的編程語(yǔ)言之一。近日,Oracle發(fā)布了Java的最新版本,Java10。在這個(gè)版本中,Oracle引入109項(xiàng)新特性,其中最引人注目的就是Java的新Jit編譯器Graal。在這個(gè)編譯器中,我們可以使用Java來做Java的Jit編譯器。本文作者詳細(xì)介紹了該特性,十分值得一讀。

導(dǎo)讀:Java是最廣泛使用的編程語(yǔ)言之一。近日,Oracle發(fā)布了Java的***版本,Java10。在這個(gè)版本中,Oracle引入109項(xiàng)新特性,其中最引人注目的就是Java的新Jit編譯器Graal。在這個(gè)編譯器中,我們可以使用Java來做Java的Jit編譯器。本文作者詳細(xì)介紹了該特性,十分值得一讀。 

Introduction

對(duì)于大部分應(yīng)用開發(fā)者來說,Java編譯器指的是JDK自帶的javac指令。這一指令可將Java源程序編譯成.class文件,其中包含的代碼格式我們稱之為Java bytecode(Java字節(jié)碼)。這種代碼格式無法直接運(yùn)行,但可以被不同平臺(tái)JVM中的interpreter解釋執(zhí)行。由于interpreter效率低下,JVM中的JIT compiler(即時(shí)編譯器)會(huì)在運(yùn)行時(shí)有選擇性地將運(yùn)行次數(shù)較多的方法編譯成二進(jìn)制代碼,直接運(yùn)行在底層硬件上。Oracle的HotSpot VM便附帶兩個(gè)用C++實(shí)現(xiàn)的JIT compiler:C1及C2。

與interpreter,GC等JVM的其他子系統(tǒng)相比,JIT compiler并不依賴于諸如直接內(nèi)存訪問的底層語(yǔ)言特性。它可以看成一個(gè)輸入Java bytecode輸出二進(jìn)制碼的黑盒,其實(shí)現(xiàn)方式取決于開發(fā)者對(duì)開發(fā)效率,可維護(hù)性等的要求。Graal是一個(gè)以Java為主要編程語(yǔ)言,面向Java bytecode的編譯器。與用C++實(shí)現(xiàn)的C1及C2相比,它的模塊化更加明顯,也更加容易維護(hù)。Graal既可以作為動(dòng)態(tài)編譯器,在運(yùn)行時(shí)編譯熱點(diǎn)方法;亦可以作為靜態(tài)編譯器,實(shí)現(xiàn)AOT編譯。在Java 10中,Graal作為試驗(yàn)性JIT compiler一同發(fā)布(JEP 317)。這篇文章將介紹Graal在動(dòng)態(tài)編譯上的應(yīng)用。有關(guān)靜態(tài)編譯,可查閱JEP 295或Substrate VM。

Tiered Compilation

在介紹Graal前,我們先了解HotSpot中的tiered compilation。前面提到,HotSpot集成了兩個(gè)JIT compiler — C1及C2(或稱為Client及Server)。兩者的區(qū)別在于,前者沒有應(yīng)用激進(jìn)的優(yōu)化技術(shù),因?yàn)檫@些優(yōu)化往往伴隨著耗時(shí)較長(zhǎng)的代碼分析。因此,C1的編譯速度較快,而C2所編譯的方法運(yùn)行速度較快。在Java 7前,用戶需根據(jù)自己的應(yīng)用場(chǎng)景選擇合適的JIT compiler。舉例來說,針對(duì)偏好高啟動(dòng)性能的GUI用戶端程序則使用C1,針對(duì)偏好高峰值性能的服務(wù)器端程序則使用C2。

Java 7引入了tiered compilation的概念,綜合了C1的高啟動(dòng)性能及C2的高峰值性能。這兩個(gè)JIT compiler以及interpreter將HotSpot的執(zhí)行方式劃分為五個(gè)級(jí)別:

  • level 0:interpreter解釋執(zhí)行

  • level 1:C1編譯,無profiling

  • level 2:C1編譯,僅方法及循環(huán)back-edge執(zhí)行次數(shù)的profiling

  • level 3:C1編譯,除level 2中的profiling外還包括branch(針對(duì)分支跳轉(zhuǎn)字節(jié)碼)及receiver type(針對(duì)成員方法調(diào)用或類檢測(cè),如checkcast,instnaceof,aastore字節(jié)碼)的profiling

  • level 4:C2編譯
    其中,1級(jí)和4級(jí)為接受狀態(tài) — 除非已編譯的方法被invalidated(通常在deoptimization中觸發(fā)),否則HotSpot不會(huì)再發(fā)出該方法的編譯請(qǐng)求。 

 

上圖列舉了4種編譯模式(非全部)。通常情況下,一個(gè)方法先被解釋執(zhí)行(level 0),然后被C1編譯(level 3),再然后被得到profile數(shù)據(jù)的C2編譯(level 4)。如果編譯對(duì)象非常簡(jiǎn)單,虛擬機(jī)認(rèn)為通過C1編譯或通過C2編譯并無區(qū)別,便會(huì)直接由C1編譯且不插入profiling代碼(level 1)。在C1忙碌的情況下,interpreter會(huì)觸發(fā)profiling,而后方法會(huì)直接被C2編譯;在C2忙碌的情況下,方法則會(huì)先由C1編譯并保持較少的profiling(level 2),以獲取較高的執(zhí)行效率(與3級(jí)相比高30%)。

Graal可替換C2成為HotSpot的頂層JIT compiler,即上述level 4。與C2相比,Graal采用更加激進(jìn)的優(yōu)化方式,因此當(dāng)程序達(dá)到穩(wěn)定狀態(tài)后,其執(zhí)行效率(峰值性能)將更有優(yōu)勢(shì)。

早期的Graal同C1及C2一樣,與HotSpot是緊耦合的。這意味著每次編譯Graal均需重新編譯HotSpot。JEP 243將Graal中依賴于HotSpot的代碼分離出來,形成Java-Level JVM Compiler Interface(JVMCI)。該接口主要提供如下三種功能:

  • 響應(yīng)HotSpot的編譯請(qǐng)求,并分發(fā)給Java-Level JIT compiler

  • 允許Java-Level JIT compiler訪問HotSpot中與JIT compilation相關(guān)的數(shù)據(jù)結(jié)構(gòu),包括類,字段,方法及其profiling數(shù)據(jù)等,并提供這些數(shù)據(jù)結(jié)構(gòu)在Java層面的抽象

  • 提供HotSpot codecache的Java抽象,允許Java-Level JIT compiler部署編譯完成的二進(jìn)制代碼

綜合利用這三種功能,我們可以將Java-Level編譯器(不局限于Graal)集成至HotSpot中,響應(yīng)HotSpot發(fā)出的level 4的編譯請(qǐng)求并將編譯后的二進(jìn)制代碼部署到HotSpot的codecache中。此外,單獨(dú)利用上述第三種功能可以繞開HotSpot的編譯系統(tǒng) — Java-Level編譯器將作為上層應(yīng)用的類庫(kù)直接部署編譯后的二進(jìn)制代碼。Graal自身的單元測(cè)試便是依賴于直接部署而非等待HotSpot發(fā)出編譯請(qǐng)求;Truffle亦是通過此機(jī)制部署編譯后的語(yǔ)言解釋器。

Graal v.s. C2

前面提到,JIT Compiler并不依賴于底層語(yǔ)言特性,它僅僅是一種代碼形式到另一種代碼形式的轉(zhuǎn)換。因此,理論上任意C2中以C++實(shí)現(xiàn)的優(yōu)化均可以在Graal中通過Java實(shí)現(xiàn),反之亦然。事實(shí)上,許多C2中實(shí)現(xiàn)的優(yōu)化均被移植到Graal中,如近期由其他開發(fā)者貢獻(xiàn)的String.compareTo intrinsic的移植。當(dāng)然,局限于C++的開發(fā)/維護(hù)難度(個(gè)人猜測(cè)),許多Graal中被證明有效的優(yōu)化并沒有被成功移植到C2上,這其中就包含Graal的inlining算法及partial escape analysis(PEA)。

Inlining是指在編譯時(shí)識(shí)別callsite的目標(biāo)方法,將其方法體納入編譯范圍并用其返回結(jié)果替換原callsite。最簡(jiǎn)單直觀的例子便是Java中常見的getter/setter方法 — inlining可以將一個(gè)方法中調(diào)用getter/setter的callsite優(yōu)化成單一內(nèi)存訪問指令。Inlining被業(yè)內(nèi)戲稱為優(yōu)化之母,其原因在于它能引發(fā)更多優(yōu)化。然而在實(shí)踐中我們往往受制于編譯單元大小或編譯時(shí)間的限制,無法***制地遞歸inline。因此,inlining的算法及策略很大程度上決定了編譯器的優(yōu)劣,尤其是在使用Java 8的stream API或使用Scala語(yǔ)言的場(chǎng)景下。這兩種場(chǎng)景對(duì)應(yīng)的Java bytecode包含大量的多層單方法調(diào)用。

Graal擁有兩個(gè)inliner實(shí)現(xiàn)。社區(qū)版的inliner采用的是深度優(yōu)先的搜索方式,在分析某一方法時(shí),一旦遇到不值得inline的callsite時(shí)便回溯至該方法的調(diào)用者。Graal允許自定義策略以判斷某一callsite值不值得inline。默認(rèn)情況下,Graal會(huì)采取一種相對(duì)貪婪的策略,根據(jù)callsite的目標(biāo)方法的大小做出相應(yīng)的決定。Graal enterprise的inliner則對(duì)所有callsite進(jìn)行加權(quán)排序,其加權(quán)算法取決于目標(biāo)方法的大小以及可能引發(fā)的優(yōu)化。當(dāng)目標(biāo)方法被inline后,其包含的callsite同樣會(huì)進(jìn)入該加權(quán)隊(duì)列中。這兩種搜索方式都較為適合擁有多層單方法調(diào)用的應(yīng)用場(chǎng)景。

Escape analysis(逃逸分析,EA)是一類識(shí)別對(duì)象動(dòng)態(tài)范圍的程序分析。編譯器中常見的應(yīng)用有兩類:如果對(duì)象僅被單一線程訪問,則可去除針對(duì)該對(duì)象的鎖操作;如果對(duì)象為堆分配且僅被單一方法訪問(inlining的重要性再次體現(xiàn)),則可將該對(duì)象轉(zhuǎn)化成棧分配。后者通常伴隨著scalar replacement,即將對(duì)對(duì)象字段的訪問替換成對(duì)虛擬局部操作數(shù)的訪問,從而進(jìn)一步將對(duì)象由棧分配轉(zhuǎn)換成虛擬分配。這不僅節(jié)省了原本用于存放對(duì)象header的內(nèi)存空間,而且可以在register allocator的幫助下將(部分)對(duì)象字段存放在寄存器中,在節(jié)省內(nèi)存的同時(shí)提高執(zhí)行效率(內(nèi)存訪問轉(zhuǎn)換成寄存器訪問)。

 

Java中常見的for-each loop是EA的一大目標(biāo)客戶。我們知道for-each loop會(huì)調(diào)用被遍歷對(duì)象的iterator方法,返回一個(gè)實(shí)現(xiàn)interface Iterator的對(duì)象,并利用其hasNext及next接口進(jìn)行遍歷。Java collections中的容器類(如ArrayList)通常會(huì)構(gòu)造一個(gè)新的Iterator實(shí)例,其生命周期局限于該for-each loop中。如若Iterator實(shí)例的構(gòu)造函數(shù)以及hasNext,next方法調(diào)用(連同它們方法體中以this為receiver的方法調(diào)用,如checkForComodification())都被inline,EA會(huì)認(rèn)為該實(shí)例沒有逃逸,并采取棧分配及scalar replacement。

 

理想情況下,F(xiàn)oo.bar會(huì)被優(yōu)化成如下代碼:

 

HotSpot的C2便已應(yīng)用控制流無關(guān)的EA實(shí)現(xiàn)scalar replacement。而Graal的PEA則在此基礎(chǔ)上引入了控制流信息,將所有的堆分配操作虛擬化,并僅在對(duì)象確定逃逸的分支materialize。與C2的EA相比,PEA分析效率較低,但能夠在對(duì)象沒有逃逸的分支上實(shí)現(xiàn)scalar replacement。如下例所示,如果then-branch的執(zhí)行概率為1%,那么被PEA優(yōu)化后的代碼在99%的情況下并不會(huì)執(zhí)行堆分配,而C2的EA則100%會(huì)執(zhí)行堆分配。另一個(gè)典型的例子是渲染引擎Sunflow — 在運(yùn)行DaCapo benchmark suite所附帶的默認(rèn)workload時(shí),Graal的PEA判定約27%的堆分配(共占700M)可被虛擬化。該數(shù)字遠(yuǎn)超C2的EA。

 

Using Graal

在Java 10 (Linux/x64, macOS/x64)中,默認(rèn)情況下HotSpot仍使用C2,但通過向java命令添加-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler參數(shù)便可將C2替換成Graal。

 

Oracle Labs GraalVM是由Oracle Labs直接發(fā)布的JDK版本。它基于Java 8,并且囊括了Graal enterprise。如果對(duì)源代碼感興趣,可直接簽出Graal社區(qū)版的GitHub repo。源代碼的編譯需借助mx工具及l(fā)absjdk(注:請(qǐng)下載頁(yè)面最下方的labsjdk,直接使用GraalVM可能會(huì)導(dǎo)致編譯問題)。

 

在graal/compiler目錄下使用mx eclipseinit,mx intellijinit或mx netbeansinit可分別生成Eclipse,IntelliJ或NetBeans的工程配置文件。

 

責(zé)任編輯:龐桂玉 來源: 高可用架構(gòu)
相關(guān)推薦

2020-11-26 11:10:15

編程語(yǔ)言開發(fā)PHP

2021-02-20 07:08:44

安卓系統(tǒng)應(yīng)用安卓12

2021-03-26 13:14:48

Tailwind CS編譯器開發(fā)者

2024-06-12 08:08:08

2020-04-13 17:17:28

MySQL8.0功能

2024-03-21 08:21:34

Java 22Java 語(yǔ)言開發(fā)工具包

2019-08-06 08:20:07

編譯器工具開發(fā)者

2009-10-23 09:36:25

.Net Compac

2024-05-11 09:38:05

React編譯器React 19

2022-04-08 07:29:25

Windows 11微軟新預(yù)覽版

2024-03-08 06:58:55

TypeScript類型縮小模塊解析

2021-06-24 05:40:28

Windows 10操作系統(tǒng)微軟

2025-04-07 08:42:00

2021-07-15 05:26:22

Windows 10操作系統(tǒng)微軟

2022-06-24 06:32:46

iOS 16Beta 2

2021-06-11 05:19:19

Windows10操作系統(tǒng)微軟

2019-06-23 23:09:25

編程語(yǔ)言PythonJava

2016-11-08 18:53:08

編譯器

2017-09-19 08:47:27

編程云安全Web開發(fā)

2021-05-14 05:20:45

Windows10操作系統(tǒng)微軟
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)