Java本身是一種物件導向的語言,最顯著的特性有兩個方面,一是所謂的“書寫一次,到處執行”,能夠非常容易地獲得跨平臺能力;
另外就是垃圾收集(GC),Java通過垃圾收集器(Garbage Collector)回收分配記憶體,大部分情況下,程式設計師不需要自己操心記憶體的分配和回收。
對於“Java是解釋執行”這句話,說法不太準確。
我們開發的Java的原始碼,首先通過Javac編譯成為位元組碼(bytecode),然後,在執行時,通過Java虛擬機器(JVM)內嵌的直譯器將位元組碼解釋執行,轉換成為最終的機器碼。
但是常見的JVM,比如我們大多數情況使用的Oracle JDK提供的Hotspot JVM,都提供了JIT(Just-In-Time)編譯器,也就是通常所說的動態編譯器,JIT能夠在執行時將熱點程式碼編譯成機器碼,這種情況下部分熱點程式碼就屬於
編譯執行,而不是解釋執行了。
Javac的編譯,編譯Java原始碼生成“.class”檔案裡面實際是位元組碼,而不是可以直接執行的機器。
Java通過位元組碼和Java虛擬機器(JVM)這種跨平臺的抽象,遮蔽了作業系統和硬體的細節,這也是實現“一次編譯,到處執行”的基礎。
在執行時,JVM會通過類載入器(Class-Loader)載入位元組碼,解釋或者編譯執行。
主流Java版本中,如JDK 8實際是解釋和編譯混合的一種模式,即所謂的混合模式(-Xmixed)。
通常執行在server模式的JVM,會進行上萬次呼叫以收集足夠的資訊進行高效的編譯,client模式這個門限是1500次。
Oracle Hotspot JVM內建了兩個不同的JIT compiler,C1對應前面說的client模式,適用於對於啟動速度敏感的應用,比如普通Java桌面應用;
C2對應server模式,它的優化是為長時間執行的伺服器端應用設計的。
預設是採用所謂的分層編譯(TieredCompilation)。
Java虛擬機器啟動時,可以指定不同的引數對執行模式進行選擇。
比如,指定“-Xint”,就是告訴JVM只進行解釋執行,不對程式碼進行編譯,這種模式拋棄了JIT可能帶來的效能優勢。畢竟直譯器(interpreter)是逐條讀入,逐條解釋執行的。
與其相對應的,還有一個“-Xcomp”引數,這是告訴JVM關閉直譯器,不要進行解釋執行,或者叫作最大優化級別。那你可能會問這種模式是不是最高效啊?簡單說,還真未必。“-Xcomp”會導致JVM啟動變慢非常多,同時有些
JIT編譯器優化方式,比如分支預測,如果不進行profling,往往並不能進行有效優化。
除了我們日常最常見的Java使用模式,其實還有一種新的編譯方式,即所謂的AOT(Ahead-of-Time Compilation),直接將位元組碼編譯成機器程式碼,這樣就避免了JIT預熱等各方面的開銷,比如
Oracle JDK 9就引入了實驗性的AOT特性,並且增加了新的jaotc工具。
Java位元組碼的設計充分考慮了JIT這一即時編譯方式,可以將位元組碼直接編譯成高效能的本地機器碼。