1. 程式人生 > >Java 執行引擎(從位元組碼到機器碼)

Java 執行引擎(從位元組碼到機器碼)

通過類裝載器裝載的,被分配到JVM的執行時資料區的位元組碼會被執行引擎執行。執行引擎以指令為單位讀取Java位元組碼。它就像一個CPU一樣,一條一條地執行機器指令。每個位元組碼指令都由一個1位元組的操作碼和附加的運算元組成。執行引擎取得一個操作碼,然後根據運算元來執行任務,完成後就繼續執行下一條操作碼。

不過Java位元組碼是用一種人類可以讀懂的語言編寫的,而不是用機器可以直接執行的語言。因此,執行引擎必須把位元組碼轉換成可以直接被JVM執行的語言。位元組碼可以通過以下兩種方式轉換成合適的語言。

  • 直譯器:一條一條地讀取,解釋並且執行位元組碼指令。因為它一條一條地解釋和執行指令,所以它可以很快地解釋位元組碼,但是執行起來會比較慢。這是解釋執行的語言的一個缺點。位元組碼這種“語言”基本來說是解釋執行的。
  • 即時(Just-In-Time)編譯器:即時編譯器被引入用來彌補直譯器的缺點。執行引擎首先按照解釋執行的方式來執行,然後在合適的時候,即時編譯器把整段位元組碼編譯成原生代碼。然後,執行引擎就沒有必要再去解釋執行方法了,它可以直接通過原生代碼去執行它。執行原生代碼比一條一條進行解釋執行的速度快很多。編譯後的程式碼可以執行的很快,因為原生代碼是儲存在快取裡的。

不過,用JIT編譯器來編譯程式碼所花的時間要比用直譯器去一條條解釋執行花的時間要多。因此,如果程式碼只被執行一次的話,那麼最好還是解釋執行而不是編譯後再執行。因此,內建了JIT編譯器的JVM都會檢查方法的執行頻率,如果一個方法的執行頻率超過一個特定的值的話,那麼這個方法就會被編譯成原生代碼。

圖 7:Java編譯器和JIT編譯器

JVM規範沒有定義執行引擎該如何去執行。因此,JVM的提供者通過使用不同的技術以及不同型別的JIT編譯器來提高執行引擎的效率。

大部分的JIT編譯器都是按照下圖的方式來執行的:

圖 8: JIT編譯器

JIT編譯器把位元組碼轉換成一箇中間層表示式,一種中間層的表示方式,來進行優化,然後再把這種表示轉換成原生代碼。

Oracle Hotspot VM使用一種叫做熱點編譯器的JIT編譯器。它之所以被稱作”熱點“是因為熱點編譯器通過分析找到最需要編譯的“熱點”程式碼,然後把熱點程式碼編譯成原生代碼。如果已經被編譯成原生代碼的位元組碼不再被頻繁呼叫了,換句話說,這個方法不再是熱點了,那麼Hotspot VM會把編譯過的原生代碼從cache裡移除,並且重新按照解釋的方式來執行它。Hotspot VM分為Server VM和Client VM兩種,這兩種VM使用不同的JIT編譯器。

Figure 9: Hotspot Client VM and Server VM.

Client VM 和Server VM使用完全相同的執行時,不過如上圖所示,它們所使用的JIT編譯器是不同的。Server VM用的是更高階的動態優化編譯器,這個編譯器使用了更加複雜並且更多種類的效能優化技術。

IBM 在IBM JDK 6裡不僅引入了JIT編譯器,它同時還引入了AOT(Ahead-Of-Time)編譯器。它使得多個JVM可以通過共享快取來共享編譯過的原生代碼。簡而言之,通過AOT編譯器編譯過的程式碼可以直接被其他JVM使用。除此之外,IBM JVM通過使用AOT編譯器來提前把程式碼編譯器成JXE(Java EXecutable)檔案格式來提供一種更加快速的執行方式。

大部分Java程式的效能都是通過提升執行引擎的效能來達到的。正如JIT編譯器一樣,很多優化的技術都被引入進來使得JVM的效能一直能夠得到提升。最原始的JVM和最新的JVM最大的差別之處就是在於執行引擎。

Hotspot編譯器在1.3版本的時候就被引入到Oracle Hotspot VM裡了,JIT編譯技術在Anroid 2.2版本的時候被引入到Dalvik VM裡。

引入一種中間語言,例如位元組碼,虛擬機器執行位元組碼,並且通過JIT編譯器來提升JVM的效能的這種技術以及廣泛應用在使用中間語言的程式語言上。例如微軟的.Net,CLR(Common Language Runtime 公共語言執行時),也是一種VM,它執行一種被稱作CIL(Common Intermediate Language)的位元組碼。CLR提供了AOT編譯器和JIT編譯器。因此,用C#或者VB.NET編寫的原始碼被編譯後,編譯器會生成CIL並且CIL會執行在有JIT編譯器的CLR上。CLR和JVM相似,它也有垃圾回收機制,並且也是基於堆疊執行。

【如需轉載,請在正文中標註並保留原文連結、譯文連結和譯者等資訊,謝謝合作!】