JVM中集成了兩種編譯器,Client Compiler和Server Compiler,它們的作用也不同,Client Compiler注重啟動速度和區域的優化,Server Compiler則更加關注全域的優化,性能會更好,但由于會進行更多的全域分析,所以啟動速度會變慢,兩種編譯器有著不同的應用場景,在虛擬機中同時發揮作用,
Client Compiler
HotSpot VM帶有一個Client Compiler C1編譯器,這種編譯器啟動速度快,但是性能比較Server Compiler來說會差一些,C1會做三件事:
- 區域簡單可靠的優化,比如位元組碼上進行的一些基礎優化,方法行內、常量傳播等,放棄許多耗時較長的全域優化,
- 將位元組碼構造成高級中間表示(High-level Intermediate Representation,以下稱為HIR),HIR與平臺無關,通常采用圖結構,更適合JVM對程式進行優化,
- 最后將HIR轉換成低級中間表示(Low-level Intermediate Representation,以下稱為LIR),在LIR的基礎上會進行暫存器分配、窺孔優化(區域的優化方式,編譯器在一個基本塊或者多個基本塊中,針對已經生成的代碼,結合CPU自己指令的特點,通過一些認為可能帶來性能提升的轉換規則或者通過整體的分析,進行指令轉換,來提升代碼性能)等操作,最終生成機器碼,
Server Compiler
Server Compiler主要關注一些編譯耗時較長的全域優化,甚至會還會根據程式運行的資訊進行一些不可靠的激進優化,這種編譯器的啟動時間長,適用于長時間運行的后臺程式,它的性能通常比Client Compiler高30%以上,目前,Hotspot虛擬機中使用的Server Compiler有兩種:C2和Graal,
C2 Compiler
在Hotspot VM中,默認的Server Compiler是C2編譯器,
C2編譯器在進行編譯優化時,會使用一種控制流與資料流結合的圖資料結構,稱為Ideal Graph, Ideal Graph表示當前程式的資料流向和指令間的依賴關系,依靠這種圖結構,某些優化步驟(尤其是涉及浮動代碼塊的那些優化步驟)變得不那么復雜,
Ideal Graph的構建是在決議位元組碼的時候,根據位元組碼中的指令向一個空的Graph中添加節點,Graph中的節點通常對應一個指令塊,每個指令塊包含多條相關聯的指令,JVM會利用一些優化技術對這些指令進行優化,比如Global Value Numbering、常量折疊等,決議結束后,還會進行一些死代碼剔除的操作,生成Ideal Graph后,會在這個基礎上結合收集的程式運行資訊來進行一些全域的優化,這個階段如果JVM判斷此時沒有全域優化的必要,就會跳過這部分優化,
無論是否進行全域優化,Ideal Graph都會被轉化為一種更接近機器層面的MachNode Graph,最后編譯的機器碼就是從MachNode Graph中得的,生成機器碼前還會有一些包括暫存器分配、窺孔優化等操作,關于Ideal Graph和各種全域的優化手段會在后面的章節詳細介紹,Server Compiler編譯優化的程序如下圖所示:

Graal Compiler
從JDK 9開始,Hotspot VM中集成了一種新的Server Compiler,Graal編譯器,相比C2編譯器,Graal有這樣幾種關鍵特性:
- 前文有提到,JVM會在解釋執行的時候收集程式運行的各種資訊,然后編譯器會根據這些資訊進行一些基于預測的激進優化,比如分支預測,根據程式不同分支的運行概率,選擇性地編譯一些概率較大的分支,Graal比C2更加青睞這種優化,所以Graal的峰值性能通常要比C2更好,
- 使用Java撰寫,對于Java語言,尤其是新特性,比如Lambda、Stream等更加友好,
- 更深層次的優化,比如虛函式的行內、部分逃逸分析等,
Graal編譯器可以通過Java虛擬機引數-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler啟用,當啟用時,它將替換掉HotSpot中的C2編譯器,并回應原本由C2負責的編譯請求,
參考原文:基本功 | Java即時編譯器原理決議及實踐 - 美團技術團隊 (meituan.com)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/550623.html
標籤:Java
下一篇:返回列表
