1.java編譯器,從源代碼到字節碼的過程

無論什么語言寫的代碼,其到最后都是通過機器碼運行的,無一例外。那么對于 Java 語言來說,其從源代碼到機器碼,這中間到底發生了什么呢?這就是今天我們要聊的。

如下圖所示,編譯器可以分為:前端編譯器、JIT 編譯器和AOT編譯器。下面我們逐個講解。
image.png

前端編譯器:源代碼到字節碼

之前我們說到:對于 Java 虛擬機來說,其實際輸入的是字節碼文件,而不是 Java 文件。那么對于 Java 語言而言,其實怎么將 Java 代碼轉化成字節碼文件的呢?我們知道在 JDK 的安裝目錄里有一個 javac 工具,就是它將 Java 代碼翻譯成字節碼,這個工具我們叫做編譯器。相對于后面要講的其他編譯器,其因為處于編譯的前期,因此又被成為前端編譯器。


595137-20181212194443807-900872025.png

通過 javac 編譯器,我們可以很方便地將 java 源文件翻譯成字節碼文件。
在cmd中用javac命令即可將一個java文件編譯出一個同名的class文件。
我們再用winhex按16進制打開class文件,可以看到相關字節碼。

關于字節碼的分析,我們已經在jvm類加載機制中討論過了(魔數和版本號)

public class Demo{
   public static void main(String args[]){
        System.out.println("Hello World!");
   }
}
595137-20181212194452947-1885638860.png

我們運行 javac 命令的過程,其實就是 javac 編譯器解析 Java 源代碼,并生成字節碼文件的過程。說白了,其實就是使用 javac 編譯器把 Java 語言規范轉化為字節碼語言規范。javac 編譯器的處理過程可以分為下面四個階段:
第一個階段:詞法、語法分析。在這個階段,JVM 會對源代碼的字符進行一次掃描,最終生成一個抽象的語法樹。簡單地說,在這個階段 JVM 會搞懂我們的代碼到底想要干嘛。就像我們分析一個句子一樣,我們會對句子劃分主謂賓,弄清楚這個句子要表達的意思一樣。
第二個階段:填充符號表。我們知道類之間是會互相引用的,但在編譯階段,我們無法確定其具體的地址,所以我們會使用一個符號來替代。在這個階段做的就是類似的事情,即對抽象的類或接口進行符號填充。等到類加載階段,JVM 會將符號替換成具體的內存地址。
第三個階段:注解處理。我們知道 Java 是支持注解的,因此在這個階段會對注解進行分析,根據注解的作用將其還原成具體的指令集。
第四個階段:分析與字節碼生成。到了這個階段,JVM 便會根據上面幾個階段分析出來的結果,進行字節碼的生成,最終輸出為 class 文件。
我們一般稱 javac 編譯器為前端編譯器,因為其發生在整個編譯的前期。常見的前端編譯器有 Sun 的 javac,Eclipse JDT 的增量式編譯器(ECJ)。

JIT 編譯器:從字節碼到機器碼

當源代碼轉化為字節碼之后,其實要運行程序,有兩種選擇。一種是使用 Java 解釋器解釋執行字節碼,另一種則是使用 JIT 編譯器將字節碼轉化為本地機器代碼。
這兩種方式的區別在于,前者啟動速度快但運行速度慢,而后者啟動速度慢但運行速度快。至于為什么會這樣,其原因很簡單。因為解釋器不需要像 JIT 編譯器一樣,將所有字節碼都轉化為機器碼,自然就少去了優化的時間。而當 JIT 編譯器完成第一次編譯后,其會將字節碼對應的機器碼保存下來,下次可以直接使用。而我們知道,機器碼的運行效率肯定是高于 Java 解釋器的。所以在實際情況中,為了運行速度以及效率,我們通常采用兩者相結合的方式進行 Java 代碼的編譯執行。

在 HotSpot 虛擬機內置了兩個即時編譯器,分別稱為 Client Compiler 和Server Compiler。這兩種不同的編譯器衍生出兩種不同的編譯模式,我們分別稱之為:C1 編譯模式,C2 編譯模式。
注意:現在許多人習慣上將 Client Compiler 稱為 C1 編譯器,將 Server Compiler 稱為 C2 編譯器,但在 Oracle 官方文檔中將其描述為 compiler mode(編譯模式)。所以說 C1 編譯器、C2 編譯器只是我們自己的習慣性稱呼,并不是官方的說法。這點需要特別注意。
那么到底應該選擇 C1 編譯模式還是 C2 編譯模式呢?
實際上對于 HotSpot 虛擬機來說,其一共有三種運行模式可選,分別是:
混合模式(Mixed Mode) 。即 C1 和 C2 兩種模式混合起來使用,這是默認的運行模式。如果你想單獨使用 C1 模式或 C2 模式,使用 -client 或 -server 打開即可。
解釋模式(Interpreted Mode)。即所有代碼都解釋執行,使用 -Xint 參數可以打開這個模式。
編譯模式(Compiled Mode)。 此模式優先采用編譯,但是無法編譯時也會解釋執行,使用 -Xcomp 打開這種模式。
在命令行中輸入 java -version 可以看到,我機器上的虛擬機使用 Mixed Mode 運行模式。


595137-20181212194526306-819762977.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容