java之JIT(Just in time)
Java程序最初是通過解釋器進行解釋執行的,當虛擬機發現某個方法或代碼塊運行的特別頻繁時,會把這些代碼認定為“熱點代碼”(Hot Spot Code)。為了提高熱點代碼的執行效率,在運行時,虛擬機會把這些代碼編譯成本地平臺相關的機器碼,並進行各種層次的優化,完成這個任務的編譯器稱為即時編譯器(JIT編譯器,不是Java虛擬機內必須的部分)。
要了解HotSpot虛擬機內的即時編譯器的運作過程,要解決幾個問題:
- 為何HotSpot虛擬機要使用解釋器和編譯器並存的架構?
- 為何HotSpot虛擬機要實現兩個不同的即時編譯器?
- 程序何時使用解釋器執行?何時使用編譯器執行?
- 哪些程序代碼會被編譯成本地代碼?如何編譯?
- 如何從外部觀察即時編譯器的編譯過程和編譯結果?
0.1.解釋器和編譯器:
解釋器是一條一條的解釋執行源語言。比如php,postscritp,javascript就是典型的解釋性語言。(直接執行)
編譯器是把源代碼整個編譯成目標代碼,執行時不再需要編譯器,直接在支持目標代碼的平臺上運行,這樣執行效率比解釋執行快很多。比如C語言代碼被編譯成二進制代碼(exe程序),在windows平臺上執行。
當程序需要快速啟動和執行的時候,解釋器可以首先發揮作用,省去編譯的時間,立即執行。在程序運行後,隨著時間的推移,編譯器逐漸發揮作用,越來越多的代碼被編譯成本地代碼,可以獲取更好的執行效率。解釋器比較節約內存,編譯器的效率比較高。解釋器還可以作為編譯器激進優化操作的“逃生門”,當激進優化的假設不成立,就退回到解釋狀態繼續執行。
0.2 為何HotSpot虛擬機要實現兩個不同的即時編譯器:
HotSpot內置了兩個編譯器,分別是Client Compiler和Server Complier,或者簡稱為C1和C2編譯器。同時用到兩個編譯器的分層編譯(Tiered Compilation)策略,使用後,C1和C2同時工作,有些代碼可能多次編譯,用C1獲取更高的編譯速度,C2獲取更好的編譯質量:
- 第0層,程序解釋執行,解釋器不開啟性能監視功能(Profiling),可觸發第1層編譯。
- 第1層,也稱為C1編譯,將字節碼編譯成本地代碼,進行簡單、可靠的優化,若有必要將加入性能監控的邏輯。
- 第2層,也稱為C2編譯,也是將字節碼編譯成為本地代碼,但是會啟動一些編譯耗時較長的優化,甚至會根據性能監控進行一些不可靠的激進優化。
0.3
編譯對象和觸發條件
在運行過程中被即時編譯器編譯的“熱點代碼”有兩類,即:
- 被多次調用的方法
- 被多次執行的循環體
對第一種情況,由於是方法調用觸發的編譯,因此編譯器會以整個方法作為編譯對象,即標準的JIT編譯方式。後一種,雖然是循環體觸發的編譯動作,但編譯器依然按照整個方法(而不是單獨的循環體)作為編譯對象。這種編譯方式稱為棧上替換(On Stack Replacement,簡稱為OSR編譯)。
判斷一段代碼是不是熱點代碼,是不是需要觸發即時編譯,這樣的行為稱為熱點探測(Hot Spot Detection),目前有兩種方法:
- 基於采樣的熱點探測:采用這樣的方法的虛擬機會周期性的檢查各個線程的棧頂,如果發現某個(或某些)方法經常出現在棧頂,那這個方法就是“熱點方法”。其好處就是實現簡單、高效,還可以很容易的獲取方法調用關系(將調用棧展開即可),缺點是很難精確的確認一個方法的熱度,容易因為受到線程阻塞或別的外界因素的影響。
- 基於計數器的熱點探測:為每一個方法(甚至是代碼塊)建立計數器,統計方法的執行次數,超過一定的閾值就認為是“熱點方法”。缺點是實現起來更麻煩,需要為每個方法建立並維護計數器,並且不能直接獲取到方法的調用關系,優點是它的統計結果相對來說更加精確和嚴謹。
HotSpot虛擬機使用第二種,它為每個方法準備了兩類計數器:方法調用計數器(Invocation Counter)和回邊計數器(Back Edge Counter,用於統計一個方法中循環體代碼執行的次數)。
本文來自:https://blog.csdn.net/shengzhu1/article/details/73281722
java之JIT(Just in time)