Java | 聊一聊編譯過程(編譯前端 & 編譯後端)
2021-10-23由 彭旭銳 發表于 農業
編譯是指什麼
前言
經過前面幾篇文章的積累,相信你已經掌握了
靜態的
Class 檔案的結構,也理解了虛擬機器類載入和位元組碼執行的
動態過程
;
這篇文章,我們來聊一聊 Java 的編譯過程,你將看到從原始碼到位元組碼再到原生代碼的整個過程。請點贊,你的點贊和關注真的對我非常重要!
目錄
1。 經典程式編譯原理
將原始碼翻譯為目的碼的過程,稱為編譯過程
,經典的程式編譯過程包含以下過程:
經典編譯原理 示意圖
如果將
目的碼
理解為**中間程式碼**,就是狹義上的編譯過程。例如
*.c
檔案編譯生成
*.obj
檔案的過程,或者
*.java
檔案編譯生成
*.class
檔案的過程;
如果將
目的碼
理解為最終執行的
機器程式碼
,那就是廣義上的編譯過程。那麼還包括
*.obj
檔案連結為可執行的
*.exe
檔案的過程,或者
*.class
解釋 / 編譯為機器程式碼的過程;
經典編譯原理 示意圖
在 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 編譯器 的主要處理過程:
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 | 關於泛型能問的都在這裡了(含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 虛擬機器`都採用直譯器與編譯器並存的執行架構。當觸發即時編譯時,執行引擎(預設)不會等待即時編譯完成,而是先按解釋執行的方式繼續執行。直到即時編譯完成後,方法的入口地址才會被修改。
3.2 即時編譯器熱點探測
那麼,即時編譯器是如何探測熱點程式碼的呢?具體來說,探測的熱點程式碼有兩種::
被多次呼叫的方法 & 被多次執行的迴圈體
。使用的探測方法有:
基於取樣 & 基於計數器
:
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!