Hollis原创|深入分析Java的编译原理
GitHub 2.6k Star 的Java工程師成神之路 ,不來了解一下嗎?
GitHub 2.6k Star 的Java工程師成神之路 ,真的不來了解一下嗎?
GitHub 2.6k Star 的Java工程師成神之路 ,真的確定不來了解一下嗎?
在《Java代碼的編譯與反編譯》中,有過關(guān)于Java語言的編譯和反編譯的介紹。我們可以通過javac命令將Java程序的源代碼編譯成Java字節(jié)碼,即我們常說的class文件。這是我們通常意義上理解的編譯。
但是,字節(jié)碼并不是機(jī)器語言,要想讓機(jī)器能夠執(zhí)行,還需要把字節(jié)碼翻譯成機(jī)器指令。這個(gè)過程是Java虛擬機(jī)做的,這個(gè)過程也叫編譯。是更深層次的編譯。
在編譯原理中,把源代碼翻譯成機(jī)器指令,一般要經(jīng)過以下幾個(gè)重要步驟:
根據(jù)完成任務(wù)不同,可以將編譯器的組成部分劃分為前端(Front End)與后端(Back End)。
前端編譯主要指與源語言有關(guān)但與目標(biāo)機(jī)無關(guān)的部分,包括詞法分析、語法分析、語義分析與中間代碼生成。
后端編譯主要指與目標(biāo)機(jī)有關(guān)的部分,包括代碼優(yōu)化和目標(biāo)代碼生成等。
我們可以把將.java文件編譯成.class的編譯過程稱之為前端編譯。把將.class文件翻譯成機(jī)器指令的編譯過程稱之為后端編譯。
Java中的前端編譯
前端編譯主要指與源語言有關(guān)但與目標(biāo)機(jī)無關(guān)的部分,包括詞法分析、語法分析、語義分析與中間代碼生成。
我們所熟知的javac的編譯就是前端編譯。除了這種以外,我們使用的很多IDE,如eclipse,idea等,都內(nèi)置了前端編譯器。主要功能就是把.java代碼轉(zhuǎn)換成.class代碼。
詞法分析
詞法分析階段是編譯過程的第一個(gè)階段。這個(gè)階段的任務(wù)是從左到右一個(gè)字符一個(gè)字符地讀入源程序,將字符序列轉(zhuǎn)換為標(biāo)記(token)序列的過程。這里的標(biāo)記是一個(gè)字符串,是構(gòu)成源代碼的最小單位。在這個(gè)過程中,詞法分析器還會(huì)對(duì)標(biāo)記進(jìn)行分類。
詞法分析器通常不會(huì)關(guān)心標(biāo)記之間的關(guān)系(屬于語法分析的范疇),舉例來說:詞法分析器能夠?qū)⒗ㄌ?hào)識(shí)別為標(biāo)記,但并不保證括號(hào)是否匹配。
語法分析
語法分析的任務(wù)是在詞法分析的基礎(chǔ)上將單詞序列組合成各類語法短語,如“程序”,“語句”,“表達(dá)式”等等.語法分析程序判斷源程序在結(jié)構(gòu)上是否正確.源程序的結(jié)構(gòu)由上下文無關(guān)文法描述。
語義分析
語義分析是編譯過程的一個(gè)邏輯階段, 語義分析的任務(wù)是對(duì)結(jié)構(gòu)上正確的源程序進(jìn)行上下文有關(guān)性質(zhì)的審查,進(jìn)行類型審查。語義分析是審查源程序有無語義錯(cuò)誤,為代碼生成階段收集類型信息。
語義分析的一個(gè)重要部分就是類型檢查。比如很多語言要求數(shù)組下標(biāo)必須為整數(shù),如果使用浮點(diǎn)數(shù)作為下標(biāo),編譯器就必須報(bào)錯(cuò)。再比如,很多語言允許某些類型轉(zhuǎn)換,稱為自動(dòng)類型轉(zhuǎn)換。
中間代碼生成
在源程序的語法分析和語義分析完成之后,很多編譯器生成一個(gè)明確的低級(jí)的或類機(jī)器語言的中間表示。該中間表示有兩個(gè)重要的性質(zhì): 1.易于生成; 2.能夠輕松地翻譯為目標(biāo)機(jī)器上的語言。
在Java中,javac執(zhí)行的結(jié)果就是得到一個(gè)字節(jié)碼,而這個(gè)字節(jié)碼其實(shí)就是一種中間代碼。
PS:著名的解語法糖操作,也是在javac中完成的。
Java中的后端編譯
首先,我們大家都知道,通常通過 javac 將程序源代碼編譯,轉(zhuǎn)換成 java 字節(jié)碼,JVM 通過解釋字節(jié)碼將其翻譯成對(duì)應(yīng)的機(jī)器指令,逐條讀入,逐條解釋翻譯。很顯然,經(jīng)過解釋執(zhí)行,其執(zhí)行速度必然會(huì)比可執(zhí)行的二進(jìn)制字節(jié)碼程序慢很多。這就是傳統(tǒng)的JVM的**解釋器(Interpreter)**的功能。為了解決這種效率問題,引入了 JIT 技術(shù)。
JAVA程序還是通過解釋器進(jìn)行解釋執(zhí)行,當(dāng)JVM發(fā)現(xiàn)某個(gè)方法或代碼塊運(yùn)行特別頻繁的時(shí)候,就會(huì)認(rèn)為這是“熱點(diǎn)代碼”(Hot Spot Code)。然后JIT會(huì)把部分“熱點(diǎn)代碼”翻譯成本地機(jī)器相關(guān)的機(jī)器碼,并進(jìn)行優(yōu)化,然后再把翻譯后的機(jī)器碼緩存起來,以備下次使用。
HotSpot虛擬機(jī)中內(nèi)置了兩個(gè)JIT編譯器:Client Complier和Server Complier,分別用在客戶端和服務(wù)端,目前主流的HotSpot虛擬機(jī)中默認(rèn)是采用解釋器與其中一個(gè)編譯器直接配合的方式工作。
當(dāng) JVM 執(zhí)行代碼時(shí),它并不立即開始編譯代碼。首先,如果這段代碼本身在將來只會(huì)被執(zhí)行一次,那么從本質(zhì)上看,編譯就是在浪費(fèi)精力。因?yàn)閷⒋a翻譯成 java 字節(jié)碼相對(duì)于編譯這段代碼并執(zhí)行代碼來說,要快很多。第二個(gè)原因是最優(yōu)化,當(dāng) JVM 執(zhí)行某一方法或遍歷循環(huán)的次數(shù)越多,就會(huì)更加了解代碼結(jié)構(gòu),那么 JVM 在編譯代碼的時(shí)候就做出相應(yīng)的優(yōu)化。
在機(jī)器上,執(zhí)行java -version命令就可以看到自己安裝的JDK中JIT是哪種模式:
上圖是我的機(jī)器上安裝的jdk1.8,可以看到,他是Server Compile,但是,需要說明的是,無論是Client Complier還是Server Complier,解釋器與編譯器的搭配使用方式都是混合模式,即上圖中的mixed mode。
熱點(diǎn)檢測(cè)
上面我們說過,要想觸發(fā)JIT,首先需要識(shí)別出熱點(diǎn)代碼。目前主要的熱點(diǎn)代碼識(shí)別方式是熱點(diǎn)探測(cè)(Hot Spot Detection),有以下兩種:
1、基于采樣的方式探測(cè)(Sample Based Hot Spot Detection) :周期性檢測(cè)各個(gè)線程的棧頂,發(fā)現(xiàn)某個(gè)方法經(jīng)常出險(xiǎn)在棧頂,就認(rèn)為是熱點(diǎn)方法。好處就是簡(jiǎn)單,缺點(diǎn)就是無法精確確認(rèn)一個(gè)方法的熱度。容易受線程阻塞或別的原因干擾熱點(diǎn)探測(cè)。
2、基于計(jì)數(shù)器的熱點(diǎn)探測(cè)(Counter Based Hot Spot Detection)。采用這種方法的虛擬機(jī)會(huì)為每個(gè)方法,甚至是代碼塊建立計(jì)數(shù)器,統(tǒng)計(jì)方法的執(zhí)行次數(shù),某個(gè)方法超過閥值就認(rèn)為是熱點(diǎn)方法,觸發(fā)JIT編譯。
在HotSpot虛擬機(jī)中使用的是第二種——基于計(jì)數(shù)器的熱點(diǎn)探測(cè)方法,因此它為每個(gè)方法準(zhǔn)備了兩個(gè)計(jì)數(shù)器:方法調(diào)用計(jì)數(shù)器和回邊計(jì)數(shù)器。
方法計(jì)數(shù)器。顧名思義,就是記錄一個(gè)方法被調(diào)用次數(shù)的計(jì)數(shù)器。
回邊計(jì)數(shù)器。是記錄方法中的for或者while的運(yùn)行次數(shù)的計(jì)數(shù)器。
編譯優(yōu)化
前面提到過,JIT除了具有緩存的功能外,還會(huì)對(duì)代碼做各種優(yōu)化。說到這里,不得不佩服HotSpot的開發(fā)者,他們?cè)贘IT中對(duì)于代碼優(yōu)化真的算是面面俱到了。
這里簡(jiǎn)答提及幾個(gè)我覺得比較重要的優(yōu)化技術(shù),并不準(zhǔn)備直接展開,讀者感興趣的話,我后面再寫文章單獨(dú)介紹。
逃逸分析、 鎖消除、 鎖膨脹、 方法內(nèi)聯(lián)、 空值檢查消除、 類型檢測(cè)消除、 公共子表達(dá)式消除
總結(jié)
以上是生活随笔為你收集整理的Hollis原创|深入分析Java的编译原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BNUOJ 4067 美丽的花环 (几
- 下一篇: 用了这么多年的 Java 泛型,你对它到