網(wǎng)站模版怎么做網(wǎng)絡(luò)營銷軟文范例500
一、概述
Java程序運(yùn)行時,JVM會加載.class字節(jié)碼文件,但是字節(jié)碼并不能直接運(yùn)行在操作系統(tǒng)之上,而JVM中的執(zhí)行引擎就是負(fù)責(zé)將字節(jié)碼轉(zhuǎn)化為對應(yīng)平臺的機(jī)器碼讓CPU運(yùn)行的組件。
執(zhí)行引擎是JVM核心的組成部分之一??梢园袹VM架構(gòu)分成三部分,如下圖所示:

?執(zhí)行引擎位于JVM的最下層(圖中虛線框部分),可以粗略地看到執(zhí)行引擎負(fù)責(zé)和運(yùn)行時數(shù)據(jù)區(qū)交互。
二、計(jì)算機(jī)語言的發(fā)展史
在講解執(zhí)行引擎之前,需要知道什么是機(jī)器碼、匯編語言、高級語言以及為什么會有Java字節(jié)碼的出現(xiàn)。
2.1 機(jī)器碼
各種用 0 和 1 組成的二進(jìn)制編碼方式表示的指令,叫作機(jī)器指令碼,簡稱機(jī)器碼。
計(jì)算機(jī)發(fā)展的初始階段,人們就用機(jī)器碼編寫程序,我們也稱為機(jī)器語言。機(jī)器語言雖然能夠被計(jì)算機(jī)理解和接受,但由于可讀性差不便于人類讀寫,并且用它編程容易出差錯。使用機(jī)器碼編寫的程序一經(jīng)輸入計(jì)算機(jī),CPU可以直接讀取運(yùn)行,因此和其他語言編的程序相比,執(zhí)行速度最快。機(jī)器碼與CPU緊密相關(guān),所以不同種類的CPU所對應(yīng)的機(jī)器碼也就不同。
2.2 匯編語言
由于機(jī)器碼是由0和1組成的二進(jìn)制序列,可讀性實(shí)在太差,于是人們發(fā)明了指令。
指令就是把機(jī)器碼中特定的0和1序列,簡化成對應(yīng)的指令(一般為英文簡寫,如mov、inc等),可讀性稍好,這就是我們常說的匯編語言。在匯編語言中,用助記符 (Mnemonics) 代替機(jī)器碼的操作碼,用地址符號 (Symbol) 或標(biāo)號 (Label) 代替指令或操作數(shù)的地址。
不同的硬件平臺,各自支持的指令是有差別的。因此每個平臺所支持的指令,稱為對應(yīng)平臺的指令集,如常見的x86指令集對應(yīng)的是x86架構(gòu)的平臺,ARM指令集對應(yīng)的是ARM架構(gòu)的平臺。不同平臺之間指令不可以直接移植。
由于計(jì)算機(jī)只認(rèn)識機(jī)器碼,所以用匯編語言編寫的程序還必須經(jīng)過匯編器轉(zhuǎn)換為機(jī)器語言,計(jì)算機(jī)才能識別和執(zhí)行。
2.3 高級語言
為了使計(jì)算機(jī)用戶編程序更容易些,后來就出現(xiàn)了各種高級計(jì)算機(jī)語言。比如C、C++等更容易讓人識別的語言。
高級語言則相對匯編語言更易于編寫,有更好的可讀性,一般編譯器會將高級語言轉(zhuǎn)換成匯編語言,然后再由匯編器轉(zhuǎn)換為機(jī)器指令由計(jì)算機(jī)執(zhí)行。

2.4 字節(jié)碼
字節(jié)碼是一種中間狀態(tài)(中間碼)的二進(jìn)制代碼(文件),需要轉(zhuǎn)譯后才能成為機(jī)器碼。Java 程序可以通過編譯器將源碼編譯成 Java 字節(jié)碼,特定平臺上的虛擬機(jī)將字節(jié)碼轉(zhuǎn)譯為可以直接執(zhí)行的指令,也就實(shí)現(xiàn)了跨平臺性。如下圖所示:

三、執(zhí)行引擎工作過程
- 執(zhí)行引擎在執(zhí)行過程中,需要執(zhí)行什么樣的字節(jié)碼指令取決于PC寄存器(程序計(jì)數(shù)器);
- 每執(zhí)行完一項(xiàng)指令操作之后,PC寄存器會更新下一條需要執(zhí)行的指令地址;
- 方法執(zhí)行過程中,執(zhí)行引擎可能會通過局部變量表中的對象引用準(zhǔn)確定位到 Java 堆中的對象實(shí)例信息,以及通過對象頭中的元數(shù)據(jù)信息定位到目標(biāo)對象的類型信息。
- 解釋器
- JIT編譯器
- 垃圾收集器

3.1 解釋器
解釋器就是一個運(yùn)行時的“翻譯者”,將字節(jié)碼指令翻譯為對應(yīng)平臺的本地機(jī)器指令由CPU執(zhí)行,當(dāng)一條指令執(zhí)行完成后再根據(jù)PC寄存器中記錄的下一條指令執(zhí)行解釋操作。
JVM設(shè)計(jì)者的初衷是為了滿足Java程序?qū)崿F(xiàn)跨平臺特性,因此避免采用靜態(tài)編譯的方式直接生成機(jī)器碼,從而誕生了實(shí)現(xiàn)解釋器在運(yùn)行時采用逐行解釋字節(jié)碼執(zhí)行程序的想法。
3.2 JIT(Just-In-Time)編譯器
當(dāng)執(zhí)行某些頻繁被調(diào)用的代碼(比如for循環(huán)中的代碼)時,如果按照解釋執(zhí)行,效率非常低,這種被頻繁調(diào)用的代碼成為熱點(diǎn)代碼。為了提高熱點(diǎn)代碼的執(zhí)行效率,在運(yùn)行時,JIT編譯器則會將這些代碼編譯成與本地平臺有關(guān)的機(jī)器碼,并進(jìn)行各種層次的優(yōu)化。
JIT的構(gòu)成組件包括:
- 中間代碼生成器——生成中間代碼
- 代碼優(yōu)化器——優(yōu)化中間代碼
- 目標(biāo)代碼生成器——生成機(jī)器碼或本地代碼
- 分析器——負(fù)責(zé)查找熱點(diǎn)代碼
3.2.1 JIT編譯器類型
在 Hotspot 虛擬機(jī)中內(nèi)置了兩種JIT編譯器:
- C1編譯器:主要關(guān)注點(diǎn)在于局部性能優(yōu)化,適用于執(zhí)行時間短或?qū)有阅苡幸蟮某绦?#xff0c;如:GUI應(yīng)用,C1編譯器也被稱為Client Compiler。
- C2編譯器:為長期運(yùn)行的服務(wù)端應(yīng)用程序做性能優(yōu)化的編譯器,適用于執(zhí)行時間較長或?qū)Ψ逯敌阅苡幸蟮某绦?#xff0c;C2編譯器也被稱為Server Compiler。
3.2.2 分層編譯
在 Java7 引入了分層編譯,這種方式綜合了 C1 的啟動優(yōu)勢和 C2 的峰值性能優(yōu)勢。分層編譯將 JVM 的執(zhí)行狀態(tài)分為5個層次:
- 第0層:程序解釋執(zhí)行,默認(rèn)開啟性能監(jiān)控(Profiling),如果不開啟,可觸發(fā)第二層編譯
- 第1層:C1編譯,將字節(jié)碼編譯成本地代碼,進(jìn)行簡單可靠的優(yōu)化,不開啟Profiling
- 第2層:C1編譯,開啟Profiling,僅執(zhí)行帶方法調(diào)用次數(shù)和循環(huán)回邊執(zhí)行次數(shù)Profiling的C1編譯
- 第3層:C1編譯,執(zhí)行所有帶Profiling的C1編譯
- 第4層:C2編譯,將字節(jié)碼編譯為本地代碼,但會啟用一下編譯耗時較長的優(yōu)化,甚至?xí)鶕?jù)性能監(jiān)控信息進(jìn)行一些不可靠的激進(jìn)優(yōu)化。
Java8 中默認(rèn)開啟分層編譯,-XX:-TieredCompilation 參數(shù)可關(guān)閉分層編譯只使用C2編譯,如果只使用C1編譯可設(shè)置參數(shù) -XX:TieredStopAtLevel=1。
-Xint參數(shù)可設(shè)置為強(qiáng)制解釋器模式運(yùn)行,-Xcomp可設(shè)置為強(qiáng)制運(yùn)行JIT編譯模式。
3.2.3 熱點(diǎn)代碼探測
Hotspot 虛擬機(jī)判定熱點(diǎn)代碼基于2種計(jì)數(shù)器進(jìn)行,方法調(diào)用計(jì)數(shù)器和回邊計(jì)數(shù)器,只有代碼符合標(biāo)準(zhǔn)并達(dá)到設(shè)置的閾值才會進(jìn)行JIT編譯優(yōu)化。
1)方法調(diào)用計(jì)數(shù)器
當(dāng)某個方法調(diào)用次數(shù)達(dá)到閾值就會觸發(fā)JIT編譯優(yōu)化。
jinfo -flag CompileThreshold 或者 java -XX:+PrintFlagsFinal -version 命令可查看方法調(diào)用次數(shù)閾值,客戶端模式下默認(rèn)值為1500,服務(wù)端模式下默認(rèn)值為10000。

2)回邊計(jì)數(shù)器
用于統(tǒng)計(jì)方法體中循環(huán)體代碼執(zhí)行次數(shù),字節(jié)碼中遇到控制流向后跳轉(zhuǎn)的指令稱為“回邊”(Back Edge),用于計(jì)算是否為熱點(diǎn)代碼的閾值。
計(jì)算公式如下:
回邊計(jì)數(shù)器閾值 = 方法調(diào)用計(jì)數(shù)器閾值(CompileThreshold)*(OSR比率(OnStackReplacePercentage)-解釋器監(jiān)控比例(InterpreterProfilePercentage))/ 100
java -XX:+PrintFlagsFinal -version 命令可查看相關(guān)參數(shù)



?根據(jù)圖中顯示的各參數(shù)的默認(rèn)值可以計(jì)算出回邊計(jì)數(shù)器閾值為:1000 * (140 - 33) = 10700
3.2.4 編譯優(yōu)化技術(shù)
1)方法內(nèi)聯(lián)
方法內(nèi)聯(lián)的優(yōu)化行為是將目標(biāo)方法的代碼復(fù)制到發(fā)起調(diào)用的方法中,避免真實(shí)方法調(diào)用。
private int add(int a, int b, int c, int d) {return addInt1(a, b) + addInt2(c, d);
}
?
private int addInt1(int a, int b) {return a + b;
}
?
private int addInt2(int a, int b) {return a + b;
}
例如上面的代碼如果被檢測為熱點(diǎn)代碼,則會被優(yōu)化為以下代碼:
private int add(int a, int b, int c, int d) {return a + b + c + d;
}
熱點(diǎn)方法不一定都會被內(nèi)聯(lián)優(yōu)化,只有當(dāng)方法體大小小于參數(shù) -XX:FreqInlineSize 值(默認(rèn)325字節(jié))才會進(jìn)行內(nèi)聯(lián),非熱點(diǎn)方法當(dāng)方法體小于參數(shù) -XX:MaxInlineSize 值(默認(rèn)35字節(jié))才會進(jìn)行內(nèi)聯(lián)。
相關(guān)性能調(diào)優(yōu) :
- 減小熱點(diǎn)方法檢測閾值,增加內(nèi)聯(lián)方法體閾值,缺點(diǎn)則是會增加內(nèi)存占用
- 盡量避免在一個方法體內(nèi)寫入大量代碼,習(xí)慣使用小方法體
- 盡量使用final private static 關(guān)鍵字修飾方法,代碼優(yōu)化時,因?yàn)槔^承需要額外的類型檢查。
2)鎖消除
當(dāng)方法中的局部方法中創(chuàng)建的對象只能被當(dāng)前線程訪問時,不存在鎖競爭,JIT編譯會對這個對象的方法進(jìn)行鎖消除。
參數(shù) -XX:+EliminateLocks 可以開啟鎖消除(默認(rèn)開啟),-XX:-EliminateLocks 則是關(guān)閉鎖消除。
3)鎖粗化
如果檢測到同一個對象執(zhí)行了連續(xù)的加鎖和解鎖操作,則會將這一系列操作合并成一個更大的鎖,從而提升程序運(yùn)行效率。
4)逃逸分析
JIT編譯器會對熱點(diǎn)代碼中的對象進(jìn)行逃逸分析,分析該對象動態(tài)作用域,當(dāng)被傳遞到其他方法中稱為方法逃逸,當(dāng)能被外部方法所引用則為線程逃逸。
不逃逸 到 方法逃逸 再到 線程逃逸,逃逸程度由低到高。
逃逸分析可以通過參數(shù) -XX:+DoEscapeAnalysis開啟(jdk1.8默認(rèn)開啟),或 -XX:-DoEscapeAnalysis 關(guān)閉。
關(guān)閉逃逸分析會導(dǎo)致對象分配到堆中,頻繁觸發(fā)垃圾回收導(dǎo)致代碼運(yùn)行慢。
5)標(biāo)量替換
當(dāng)確定對象不會逃逸出線程之外,該對象則會被分配到棧上,對象分配到棧需要進(jìn)行成員變量拆分,這種優(yōu)化技術(shù)叫做標(biāo)量替換。標(biāo)量替換需要開啟逃逸分析。
標(biāo)量替換可以通過參數(shù) -XX:+EliminateAllocations開啟(jdk1.8默認(rèn)開啟),或-XX:+EliminateAllocations關(guān)閉
3.3 垃圾收集器
Java的一個主要特點(diǎn)就是開發(fā)人員不需要過分關(guān)注對象的內(nèi)存管理,無用對象的銷毀和空間釋放都交由JVM的垃圾收集器處理。這減輕了編程負(fù)擔(dān)提高了編程效率,但垃圾回收也會影響程序的性能。
詳細(xì)介紹見上章。
四、總結(jié)
JVM中的執(zhí)行引擎是JVM的重要組成部分之一,主要負(fù)責(zé)將字節(jié)碼指令翻譯成機(jī)器碼指令。
執(zhí)行引擎包括解釋器,JIT解釋器和垃圾收集器。
解釋器就是運(yùn)行時的“翻譯者”,將字節(jié)碼解釋為機(jī)器指令。
但是某些頻繁執(zhí)行的熱點(diǎn)代碼依然采用解釋執(zhí)行的話會導(dǎo)致程序執(zhí)行很慢,JIT編譯器則是負(fù)責(zé)將熱點(diǎn)代碼編譯分層優(yōu)化成本地機(jī)器碼。
垃圾收集器則負(fù)責(zé)無用對象的銷毀,釋放內(nèi)存空間。