農林漁牧網

您現在的位置是:首頁 > 農業

Java | 聊一聊編譯過程(編譯前端 & 編譯後端)

2021-10-23由 彭旭銳 發表于 農業

編譯是指什麼

前言

經過前面幾篇文章的積累,相信你已經掌握了

靜態的

Class 檔案的結構,也理解了虛擬機器類載入和位元組碼執行的

動態過程

這篇文章,我們來聊一聊 Java 的編譯過程,你將看到從原始碼到位元組碼再到原生代碼的整個過程。請點贊,你的點贊和關注真的對我非常重要!

目錄

Java | 聊一聊編譯過程(編譯前端 & 編譯後端)

1。 經典程式編譯原理

將原始碼翻譯為目的碼的過程,稱為編譯過程

,經典的程式編譯過程包含以下過程:

Java | 聊一聊編譯過程(編譯前端 & 編譯後端)

經典編譯原理 示意圖

如果將

目的碼

理解為**中間程式碼**,就是狹義上的編譯過程。例如

*.c

檔案編譯生成

*.obj

檔案的過程,或者

*.java

檔案編譯生成

*.class

檔案的過程;

如果將

目的碼

理解為最終執行的

機器程式碼

,那就是廣義上的編譯過程。那麼還包括

*.obj

檔案連結為可執行的

*.exe

檔案的過程,或者

*.class

解釋 / 編譯為機器程式碼的過程;

Java | 聊一聊編譯過程(編譯前端 & 編譯後端)

經典編譯原理 示意圖

在 Java 技術下,除非有特定的上下文語境,編譯一詞通常指的是

*.java

轉換為

*.class

的過程,這個過程也被稱為

編譯前端

。除此之外,編譯一詞還可以指執行期即時編譯(JIT,Just in Time Compile)或者(靜態的)提前編譯(AOT,Ahead of Time Compile),這兩種編譯稱為

編譯後端

下面,我們整理一下

編譯前端 & 編譯後端

的要點:

2。 編譯前端

編譯前端階段中,最重要的一個編譯器就是 javac 編譯器, 在命令列執行

javac

命令,其實本質是運行了

javac.exe

這個應用。`Android`工程師可能對於 Gradle 構建過程更為熟悉,構建過程中有一個

Task:compileDebugJavaWithJavac

,其實也用到了 javac 編譯器,編譯中間產物路徑在

build/intermediates/javac/debug/classes

2.1 javac 編譯

下面,我們整理 javac 編譯器 的主要處理過程:

Java | 聊一聊編譯過程(編譯前端 & 編譯後端)

javac 編譯過程

延伸文章:

- 關於**註解處理器**,請閱讀:[《Java | 註解處理器原理解析與實踐》](https://www。jianshu。com/p/993206508d35)

- 關於**類載入**,請閱讀:[《Java | 談談你對類載入過程的理解》](https://www。jianshu。com/p/993206508d35)

- 關於**方法呼叫**,請閱讀:[《Java | 深入理解方法呼叫的本質(含過載與重寫區別)》](https://www。jianshu。com/p/b5b919f24f82)

2.2 Java 常用語法

語法糖

指的是高階語言中的某種語法,這些語法糖在編譯時進行

解語法糖

,轉換為無糖語法。這些語法糖大多都是靠編譯器實現,而不是依賴位元組碼或者虛擬機器的底層支援。下表總結了部分熟知的語法糖:

Java | 聊一聊編譯過程(編譯前端 & 編譯後端)

Java 常見語法糖 示例

延伸文章:

- 關於**泛型**,請閱讀:[《Java | 關於泛型能問的都在這裡了(含Kotlin)》](https://www。jianshu。com/writer#/notebooks/39010758/notes/75762113)

- 關於**內部類**,請閱讀:[《Java | 為什麼非靜態內部類會持有外部類的引用》](https://www。jianshu。com/p/993206508d35)

- 關於**switch**,請閱讀:[《Java | switch 和 if-else 哪個更高效》](https://www。jianshu。com/p/993206508d35)

3。 編譯後端

在編譯後端階段中,最重要的是

執行

即時編譯器

(JIT,Just in Time Compiler)和

靜態的提前編譯器

(AOT,Ahead of Time Compiler)。

3.1 解釋執行 & 編譯執行

根據前面的內容,我們知道編譯前端的核心編譯產物是:Class 檔案。但是對於`CPU`來說,它是不認得位元組碼的。在

Java | 為什麼 Java 實現了平臺無關性?

裡,我們強調了:

每種 CPU 只能“讀懂”自身支援的機器語言或者原生代碼(native code)。

因此,Java 虛擬機器在執行位元組碼時,需要

將位元組碼翻譯為當前平臺的原生代碼

,可以分為:

解釋執行 & 編譯執行

,具體如下:

Java | 聊一聊編譯過程(編譯前端 & 編譯後端)

需要注意的是,並不是所有的`Java 虛擬機器`都採用直譯器與編譯器並存的執行架構。當觸發即時編譯時,執行引擎(預設)不會等待即時編譯完成,而是先按解釋執行的方式繼續執行。直到即時編譯完成後,方法的入口地址才會被修改。

3.2 即時編譯器熱點探測

那麼,即時編譯器是如何探測熱點程式碼的呢?具體來說,探測的熱點程式碼有兩種::

被多次呼叫的方法 & 被多次執行的迴圈體

。使用的探測方法有:

基於取樣 & 基於計數器

Java | 聊一聊編譯過程(編譯前端 & 編譯後端)

3.3 編譯器最佳化技術

Editting。。。

4。 總結

編譯前端

的最佳化措施主要目的是降低程式設計師的編碼複雜度,提高編碼效率。語法糖並不是不是依賴位元組碼或者虛擬機器的底層支援,在編譯後會轉換為基礎的無糖語法。另外,註解處理器相當於編譯器的外掛,開發人員可以透過它對前端編譯施加影響。

編譯後端

的最佳化措施主要目的是生成更高效的機器程式碼,提高執行效率。提前編譯在程式執行前編譯位元組碼,相當於提前預熱。而即時編譯器在執行時動態監控程式執行,探測得熱點程式碼後編譯為原生代碼,生成的程式碼更高效,但需要預熱期。

參考資料

《深入理解Java虛擬機器(第3版本)》(第10、11章)—— 周志明 著

《深入理解Android:Java虛擬機器 ART》 —— 鄧凡平 著

《深入理解 JVM 位元組碼》(第4、5章)—— 張亞 著

推薦閱讀

Java | 為什麼 Java 實現了平臺無關性?

- [Java | Object obj = new Object()佔用多少位元組?](https://www。jianshu。com/p/70557f0100ff)

- [Java | 帶你理解 ServiceLoader 的原理與設計思想](https://www。jianshu。com/p/a18499b5df1c)

- [Android | 一個程序有多少個 Context 物件(答對的不多)](https://www。jianshu。com/p/51d63a1ffb95)

- [Android | 帶你理解 NativeAllocationRegistry 的原理與設計思想](https://www。jianshu。com/p/6f042f9e47a8)

- [Android | 談一談 Matrix 與座標變換](https://www。jianshu。com/p/d61a785af776)

- [Android | 一文帶你全面瞭解 AspectJ 框架](https://www。jianshu。com/p/266e5a918c84)

- [計算機組成原理 | Unicode 和 UTF-8是什麼關係?](https://www。jianshu。com/p/6274b13e7982)

- [計算機組成原理 | 為什麼浮點數運算不精確?(阿里筆試)](https://www。jianshu。com/p/2adb8fe74987)

感謝喜歡!你的點贊和關注真的對我非常重要!歡迎關注[彭旭銳](https://github.com/pengxurui)的Github!

Java | 聊一聊編譯過程(編譯前端 & 編譯後端)