什麼是JIT?
什麼是JIT?
1、動態編譯(dynamic compilation)指的是「在運行時進行編譯」;與之相對的是事前編譯(ahead-of-time compilation,簡稱AOT),也叫靜態編譯(static compilation)。
2、JIT 編譯(just-in-time compilation)狹義來說是當某段程式碼即將第一次被執行時進行編譯,因而叫「即時編譯」。JIT編譯是動態編譯的一種特例。JIT編譯一詞後來被泛化,時常與動態編譯等價;但要注意廣義與狹義的JIT編譯所指的區別。
3、自適應動態編譯(adaptive dynamic compilation)也是一種動態編譯,但它通常執行的時機比JIT編譯遲,先讓程式「以某種式」先運行起來,收集一些資訊之後再做動態編譯。這樣的編譯可以更加優化。
在部分商用虛擬機中(如HotSpot),Java程式最初是通過解釋器(Interpreter)進行解釋執行的,當虛擬機發現某個方法或程式碼塊的運行特別頻繁時,就會把這些程式碼認定為「熱點程式碼」。為了提高熱點程式碼的執行效率,在運行時,虛擬機將會把這些程式碼編譯成與本地平台相關的機器碼,並進行各種層次的優化,完成這個任務的編譯器稱為即時編譯器(Just In Time Compiler,下文統稱JIT編譯器)。
即時編譯器並不是虛擬機必須的部分,Java虛擬機規範並沒有規定Java虛擬機內必須要有即時編譯器存在,更沒有限定或指導即時編譯器應該如何去實現。但是,即時編譯器編譯性能的好壞、程式碼優化程度的高低卻是衡量一款商用虛擬機優秀與否的最關鍵的指標之一,它也是虛擬機中最核心且最能體現虛擬機技術水平的部分。
由於Java虛擬機規範並沒有具體的約束規則去限制即使編譯器應該如何實現,所以這部分功能完全是與虛擬機具體實現相關的內容,如無特殊說明,我們提到的編譯器、即時編譯器都是指Hotspot虛擬機內的即時編譯器,虛擬機也是特指HotSpot虛擬機。
為什麼HotSpot虛擬機要使用解釋器與編譯器並存的架構?
儘管並不是所有的Java虛擬機都採用解釋器與編譯器並存的架構,但許多主流的商用虛擬機(如HotSpot),都同時包含解釋器和編譯器。
解釋器與編譯器兩者各有優勢:當程式需要 迅速啟動和執行 的時候,解釋器可以首先發揮作用,省去編譯的時間,立即執行。在程式運行後,隨著時間的推移,編譯器逐漸發揮作用,把越來越多的程式碼編譯成本地程式碼之後,可以獲取 更高的執行效率 。當程式運行環境中 記憶體資源限制較大 (如部分嵌入式系統中),可以使用 解釋器執行節約記憶體 ,反之可以使用 編譯執行來提升效率 。此外,如果編譯後出現「罕見陷阱」,可以通過逆優化退回到解釋執行。
HotSpot虛擬機中內置了兩個即時編譯器:Client Complier和Server Complier,簡稱為C1、C2編譯器,分別用在客戶端和服務端。目前主流的HotSpot虛擬機中默認是採用解釋器與其中一個編譯器直接配合的方式工作。程式使用哪個編譯器,取決於虛擬機運行的模式。HotSpot虛擬機會根據自身版本與宿主機器的硬體性能自動選擇運行模式,用戶也可以使用「-client」或「-server」參數去強制指定虛擬機運行在Client模式或Server模式。
用Client Complier獲取更高的編譯速度,用Server Complier 來獲取更好的編譯品質。為什麼提供多個即時編譯器與為什麼提供多個垃圾收集器類似,都是為了適應不同的應用場景。
編譯的時間開銷
解釋器的執行,抽象的看是這樣的:
*輸入的程式碼 -> [ 解釋器 解釋執行 ] -> 執行結果
而要JIT編譯然後再執行的話,抽象的看則是:
*輸入的程式碼 -> [ 編譯器 編譯 ] -> 編譯後的程式碼 -> [ 執行 ] -> 執行結果
*說JIT比解釋快,其實說的是「執行編譯後的程式碼」比「解釋器解釋執行」要快,並不是說「編譯」這個動作比「解釋」這個動作快。
JIT編譯再怎麼快,至少也比解釋執行一次略慢一些,而要得到最後的執行結果還得再經過一個「執行編譯後的程式碼」的過程。所以,對「只執行一次」的程式碼而言,解釋執行其實總是比JIT編譯執行要快。
怎麼算是「只執行一次的程式碼」呢?粗略說,下面兩個條件同時滿足時就是嚴格的「只執行一次」
1、只被調用一次,例如類的構造器(class initializer,
2、沒有循環
對只執行一次的程式碼做JIT編譯再執行,可以說是得不償失。
對只執行少量次數的程式碼,JIT編譯帶來的執行速度的提升也未必能抵消掉最初編譯帶來的開銷。
只有對頻繁執行的程式碼,JIT編譯才能保證有正面的收益。