JAVA虛擬機(jī)(java virtual machine,JVM),一種能夠運(yùn)行java字節(jié)碼的虛擬機(jī)。作為一種編程語(yǔ)言的虛擬機(jī),實(shí)際上 不只是專(zhuān)用于Java語(yǔ)言,只要生成的編譯文件匹配JVM對(duì)加載編譯文件格式要求,任何語(yǔ)言都可以由JVM編譯運(yùn)行。 比如kotlin、scala等。 另外JVM有很多種,不只是Hotspot,還有JRockit、J9等等。
JAVA的優(yōu)勢(shì)主要在于它的設(shè)計(jì)理念:write once, run anywhere。
雖然C++之類(lèi)的語(yǔ)言會(huì)編譯其源代碼但只能匹配特定的操作系統(tǒng)和CPU硬件,但是JAVA是使用JAVA編譯器(javac),將JAVA源代碼編譯為字節(jié)碼(.class)文件。該字節(jié)碼為16進(jìn)制格式,帶有操作數(shù)、操作碼。JVM可以根據(jù)本機(jī)的運(yùn)行環(huán)境將這些操作指令解釋為本機(jī)的操作系統(tǒng)和CPU可以理解的機(jī)器語(yǔ)言(無(wú)需進(jìn)一步的編譯)。
因此字節(jié)碼充當(dāng)了獨(dú)立平臺(tái)的存在,可以在任何JVM之間移植,進(jìn)而與操作系統(tǒng)和硬件隔離開(kāi),但是JVM也是基于操作系統(tǒng)和基礎(chǔ)硬件開(kāi)發(fā)的,JVM底層需要與操作系統(tǒng)和底層硬件進(jìn)行交互,所以我們需要根據(jù)操作系統(tǒng)和CPU結(jié)果選擇合適的JVM版本。(Win32,Win64,MacOS,Linux)
JVM由三個(gè)主要的子系統(tǒng)組成
- 類(lèi)加載子系統(tǒng)
- 運(yùn)行時(shí)數(shù)據(jù)區(qū)
-
執(zhí)行引擎
JRE結(jié)構(gòu).png
類(lèi)加載子系統(tǒng)(Class Loader SubSystem)
JVM運(yùn)行在內(nèi)存中。JAVA程序在執(zhí)行期間,會(huì)使用類(lèi)加載子系統(tǒng)將需要的類(lèi)加載到內(nèi)存中。這種行為被成為動(dòng)態(tài)類(lèi)加載功能。在程序運(yùn)行時(shí)(非編譯)它會(huì)加載、鏈接、初始化class文件(.class)。

類(lèi)的生命周期

1.加載(Loading)
將編譯后的class文件加載到內(nèi)存是Class Loader的主要任務(wù)。
通常類(lèi)加載過(guò)程從加載主類(lèi)(即程序入口類(lèi))開(kāi)始,后續(xù)的類(lèi)加載都是在已運(yùn)行的類(lèi)中的類(lèi)引用完成的,比如以下情況
- 當(dāng)調(diào)用一個(gè)靜態(tài)方法的時(shí)候(System.out)
- 當(dāng)創(chuàng)建一個(gè)對(duì)象的時(shí)候(Person p = new Person)
類(lèi)加載器主要有三種類(lèi)型,它們遵循以下四個(gè)原則
- 可見(jiàn)性原則(Visibility Principle): 子類(lèi)加載器可以看到父類(lèi)加載器加載的類(lèi),但是父類(lèi)加載器找不到子類(lèi)加載器加載的類(lèi)。
- 唯一性原則(Uniqueness Principle): 父類(lèi)加載器加載的類(lèi)不應(yīng)再由子類(lèi)加載器加載,并且確保不會(huì)發(fā)生重復(fù)的加載
- 層次委托原則(Delegation Hierarchy Principle): 為了滿(mǎn)足上述兩個(gè)原則,JVM遵循委托的層次結(jié)構(gòu)來(lái)為每個(gè)類(lèi)裝入請(qǐng)求選擇類(lèi)裝入器。
從最低的子級(jí)別開(kāi)始,應(yīng)用程序類(lèi)加載器(Application ClassLoader)將接收到的類(lèi)加載請(qǐng)求委托給擴(kuò)展類(lèi)加載器(Extension ClassLoader),然后擴(kuò)展類(lèi)加載器將請(qǐng)求委托給啟動(dòng)類(lèi)加載器(Bootstrap ClassLoader)。如果在Bootstrap路徑中找到了所請(qǐng)求的類(lèi),則將加載該類(lèi)。否則,請(qǐng)求將再次轉(zhuǎn)移回?cái)U(kuò)展類(lèi)加載器級(jí)別,以從擴(kuò)展路徑或自定義指定的路徑中查找類(lèi)。如果它也失敗,則請(qǐng)求返回到應(yīng)用程序類(lèi)加載器,以從System類(lèi)路徑中查找該類(lèi),并且如果應(yīng)用程序類(lèi)加載器也未能加載所請(qǐng)求的類(lèi),則我們將獲得運(yùn)行時(shí)異常 — java.lang.ClassNotFoundException。
- 無(wú)卸載原則(No Unloading Principle): 類(lèi)加載器無(wú)法卸載已經(jīng)加載的類(lèi)。除了不能卸載之外,可以刪除當(dāng)前的類(lèi)加載器,并創(chuàng)建一個(gè)新的類(lèi)加載器。
類(lèi)加載機(jī)制

啟動(dòng)類(lèi)加載器(Boostrap ClassLoader):負(fù)責(zé)加載JRE的核心類(lèi)庫(kù),
$JAVA_HOME/jre/lib。
擴(kuò)展類(lèi)加載器(Extension ClassLoader):負(fù)責(zé)加載JRE擴(kuò)展目錄$JAVA_HOME/jre/lib/ext或者java.ext指定的任何其他目錄中加載類(lèi)。
應(yīng)用程序加載器(Applicaiton ClassLoader):從類(lèi)路徑(claasspath)加載應(yīng)用程序特定的類(lèi)。
除了上述的三種類(lèi)加載器之外,還可以自定義類(lèi)加載器。類(lèi)加載委托模型保證了應(yīng)用程序的獨(dú)立性。這種自定義的類(lèi)加載器常用于Tomcat等web應(yīng)用程序中。
2.連接(Linking)
連接涉及驗(yàn)證和準(zhǔn)備加載的類(lèi)、接口、父類(lèi)、父接口以及元素類(lèi)型,同時(shí)具有以下屬性
- 在連接一個(gè)類(lèi)或者接口之前,必須將其完全加載
- 在初始化類(lèi)或接口之前,必須對(duì)其進(jìn)行完全驗(yàn)證和準(zhǔn)備
- 如果在鏈接過(guò)程中發(fā)生錯(cuò)誤,則會(huì)在程序中的某個(gè)位置引發(fā)錯(cuò)誤,該錯(cuò)誤將由程序執(zhí)行,而這些操作可能直接或間接地需要鏈接到錯(cuò)誤所涉及的類(lèi)或接口
連接分為以下三個(gè)階段
2.1 驗(yàn)證(Verification)
? 驗(yàn)證.class文件是否正確(是否符合java語(yǔ)言規(guī)范?是否由有效的編譯器根據(jù)JVM規(guī)范生成?)。這是類(lèi)加載過(guò)程中最復(fù)雜的測(cè)試過(guò)程,并且耗時(shí)最長(zhǎng)。即使連接減慢了類(lèi)加載過(guò)程的速度,它也避免了在執(zhí)行字節(jié)碼時(shí)多次執(zhí)行這些檢查的需要,從而使整體執(zhí)行高效而有效。如果驗(yàn)證失敗,則會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤(java.lang.VerifyError)。
# 例如檢查如下
- 一致且格式正確的符號(hào)表
- 不覆蓋最終方法/類(lèi)
- 方法遵循訪問(wèn)控制關(guān)鍵字
- 方法具有正確的數(shù)量和參數(shù)類(lèi)型
- 字節(jié)碼不會(huì)錯(cuò)誤地操作堆棧
- 變量在讀取前已初始化
- 變量的值為正確的類(lèi)型
2.2 準(zhǔn)備(Preparation)
? 為靜態(tài)變量和JVM中使用的任何數(shù)據(jù)結(jié)構(gòu)(比如方法表)分配內(nèi)存。靜態(tài)字段已創(chuàng)建并初始化為其默認(rèn)值,但是在此階段不執(zhí)行任何初始化程序或代碼,因?yàn)檫@是初始化的一部分。
2.3 解析(Resolution)
? 用直接引用替換類(lèi)型中的符號(hào)引用。通過(guò)搜索方法區(qū)域以找到引用的實(shí)體來(lái)完成此操作。
3.初始化(Initialization)
? 在這里將執(zhí)行每個(gè)加載的類(lèi)或者接口的初始化邏輯(比如調(diào)用類(lèi)的構(gòu)造函數(shù)),由于JVM是多線(xiàn)程的,因此在適當(dāng)同步的情況下非常仔細(xì)地進(jìn)行類(lèi)或接口的初始化,以避免其他線(xiàn)程嘗試初始化同一個(gè)類(lèi)或接口(即時(shí)線(xiàn)程是安全的)。
? 這是類(lèi)加載的最后階段,所有靜態(tài)變量都分配有程序中定義的值,并且將執(zhí)行靜態(tài)塊(如果有)。在類(lèi)中從上到下,從類(lèi)層次結(jié)構(gòu)中的父級(jí)到子級(jí)逐級(jí)執(zhí)行。
運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area)
? 運(yùn)行時(shí)數(shù)據(jù)區(qū)是JVM在程序在操作系統(tǒng)上運(yùn)行時(shí)分配的存儲(chǔ)區(qū)(內(nèi)存)。除了讀取.class文件之外,類(lèi)加載子系統(tǒng)還會(huì)生成相應(yīng)的class二進(jìn)制數(shù)據(jù),并將每一個(gè)類(lèi)的以下信息保存在方法區(qū)。
- 加載的類(lèi)及其直接父類(lèi)的全限定類(lèi)名
- .class的關(guān)聯(lián)類(lèi)型 Class / Interface / Enum / @Interface
- 修飾符、靜態(tài)變量、方法信息等
每加載一個(gè).class文件,JVM會(huì)按照java.lang包中的定義,創(chuàng)建一個(gè)Class對(duì)象來(lái)表示內(nèi)存中的文件。此Class對(duì)象可用于在代碼中讀取類(lèi)級(jí)別信息(類(lèi)名稱(chēng),父類(lèi)名稱(chēng),方法,變量信息,靜態(tài)變量等)
1.方法區(qū)(Method Area)
? 類(lèi)的所有字段和方法字節(jié)碼,以及一些特殊方法如構(gòu)造函數(shù),接口代碼也在這里定義。簡(jiǎn)單來(lái)說(shuō),所有類(lèi)定義的方法的信息都保存在該區(qū)域,靜態(tài)變量+常量+類(lèi)信息(構(gòu)造方法/接口定義)+ 運(yùn)行時(shí)常量池都存在方法區(qū)中,雖然Java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分,但是它卻有一個(gè)別名叫做Non-Heap(非堆),目的應(yīng)該是為了和Java的堆區(qū)分開(kāi)。
- 方法區(qū)是JVM中的一種規(guī)范,具體在JVM內(nèi)部的實(shí)現(xiàn)中各有不同
存儲(chǔ)以下數(shù)據(jù)
- 類(lèi)加載器的引用
- 運(yùn)行時(shí)常量池 - 數(shù)字常量 / 字段引用 / 方法引用 / 屬性;以及每個(gè)類(lèi)和接口的常量,它包含方法和字段的所有引用。引用方法或字段時(shí),JVM使用運(yùn)行時(shí)常量池在內(nèi)存中搜索該方法或字段的實(shí)際地址。
- 字段數(shù)據(jù) - 名稱(chēng),類(lèi)型,修飾符,屬性
- 方法數(shù)據(jù) - 名稱(chēng),返回類(lèi)型,參數(shù)類(lèi)型(按順序),修飾符,屬性
- 方法代碼 - 字節(jié)碼,操作數(shù)堆棧大小,局部變量大小,局部變量表,異常表;異常表中的每個(gè)異常處理程序:起點(diǎn),終點(diǎn),處理程序代碼的PC偏移,捕獲的異常類(lèi)的常量池索引
2.堆(Heap)
? 虛擬機(jī)啟動(dòng)時(shí)自動(dòng)分配創(chuàng)建,用于存放對(duì)象的實(shí)例,幾乎所有對(duì)象(包括常量池)都在堆上分配內(nèi)存,當(dāng)對(duì)象無(wú)法在該空間申請(qǐng)到內(nèi)存是將拋出OutOfMemoryError異常。同時(shí)也是垃圾收集器管理的主要區(qū)域。

2.1 新生代(Young Generation)
新生代是類(lèi)出生、成長(zhǎng)、消亡的區(qū)域,一個(gè)類(lèi)在這里產(chǎn)生,應(yīng)用,最后被垃圾回收器收集, 結(jié)束生命。新生代分為兩部分:伊甸區(qū)(Eden space)和幸存者區(qū)(Survivor space),所有的類(lèi)都是在Enden區(qū)被new出來(lái)的。幸存區(qū)又分為From和To區(qū)。當(dāng)Eden區(qū)的空間用完時(shí),程序又需要?jiǎng)?chuàng)建對(duì)象,JVM的垃圾回收器將對(duì)新生代區(qū)域進(jìn)行垃圾回收(Minor GC),將新生代區(qū)中不再被其它對(duì)象引用的對(duì)象進(jìn)行銷(xiāo)毀。然后將Eden區(qū)中剩余的對(duì)象移到From Survivor區(qū)。若From Survivor區(qū)也滿(mǎn)了,再對(duì)該區(qū)進(jìn)行垃圾回收,然后移動(dòng)到To Survivor區(qū)。
動(dòng)態(tài)年齡判斷:虛擬機(jī)并不是永遠(yuǎn)的要求對(duì)象的年齡必須達(dá)到 MaxTenuringThreshold(默認(rèn)15)才能晉升到老年代。如果在Survivor空間中相同年齡的所有對(duì)象大小總和大于Survivor空間的一半, 年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)人老年代,無(wú)須等到MaxTenuringThreshoId中要求的年齡。
2.2 老年代(Old Generation)
新生代經(jīng)過(guò)多次GC仍然存活的對(duì)象移動(dòng)到老年代區(qū)域。若老年代也滿(mǎn)了,這時(shí)候?qū)l(fā)生Major GC(也可以叫Full GC),進(jìn)行老年區(qū)的內(nèi)存清理。若老年區(qū)執(zhí)行了Full GC之后發(fā)現(xiàn)依然無(wú)法進(jìn)行對(duì)象的保存,就會(huì)拋出
OOM(OutOfMemoryError)異常。
2.3 元空間(Meta Space)
在JDK1.8之后,元空間替代了永久代,它是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn),區(qū)別在于元數(shù)據(jù)區(qū)不在虛擬機(jī)當(dāng)中,而是用的本地內(nèi)存,永久代在虛擬機(jī)當(dāng)中,永久代邏輯結(jié)構(gòu)上也屬于堆,但是物理上不屬于。
為什么移除了永久代?
參考官方解釋http://openjdk.java.net/jeps/122
大概意思是移除永久代是為融合HotSpot與 JRockit而做出的努力,因?yàn)镴Rockit沒(méi)有永久代,不需要配置永久代。

3.棧(Stack)
Java線(xiàn)程執(zhí)行方法的內(nèi)存模型一個(gè)線(xiàn)程對(duì)應(yīng)一個(gè)棧,每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(用于存儲(chǔ)局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈接,方法出口等信息),并將這個(gè)棧幀推入到棧頂。棧不存在垃圾回收問(wèn)題,只要線(xiàn)程一結(jié)束該棧就釋放,生命周期和線(xiàn)程一 致。

局部變量表 : 它的索引從0開(kāi)始。對(duì)于特定的方法,涉及多少個(gè)局部變量,并且相應(yīng)的值存儲(chǔ)在此處。0是該方法所屬的類(lèi)實(shí)例的引用。從1開(kāi)始,保存發(fā)送到方法的參數(shù)。在方法參數(shù)之后,將保存方法的局部變量。
操作數(shù)棧 : 充當(dāng)運(yùn)行時(shí)工作空間,以在需要時(shí)執(zhí)行任何中間操作。每個(gè)方法在Operand堆棧和局部變量數(shù)組之間交換數(shù)據(jù),并推送或彈出其他方法調(diào)用結(jié)果。可以在編譯期間確定操作數(shù)堆??臻g的必要大小。因此,操作數(shù)堆棧的大小也可以在編譯期間確定。
幀數(shù)據(jù) : 與該方法有關(guān)的所有符號(hào)都存儲(chǔ)在這里。作為異常,捕獲的異常信息也將保留在幀數(shù)據(jù)中。
??梢允莿?dòng)態(tài)或固定大小,如果線(xiàn)程需要比允許的棧大的棧,則會(huì)引發(fā)StackOverflowError。如果一個(gè)線(xiàn)程需要一個(gè)新的幀,并且沒(méi)有足夠的內(nèi)存來(lái)分配它,則拋出OutOfMemoryError。
4.本地方法棧(Native Method Stack)
? 和棧作用很相似,區(qū)別不過(guò)是Java棧為JVM執(zhí)行Java方法服務(wù),而本地方法棧為JVM執(zhí)行native方法服務(wù)。登記native方法,在Execution Engine執(zhí)行時(shí)加載本地方法庫(kù)。
? Java線(xiàn)程和本機(jī)操作系統(tǒng)線(xiàn)程之間存在直接映射。在為Java線(xiàn)程準(zhǔn)備好所有狀態(tài)之后,還將創(chuàng)建一個(gè)單獨(dú)的本地棧,以存儲(chǔ)通過(guò)JNI(Java本地接口)調(diào)用的本機(jī)方法信息(通常用C / C ++編寫(xiě))
? 一旦創(chuàng)建并初始化了本機(jī)線(xiàn)程,它將調(diào)用Java線(xiàn)程中的run()方法。當(dāng)run()方法返回時(shí),將處理未捕獲的異常(如果有),然后本機(jī)線(xiàn)程確認(rèn)是否由于線(xiàn)程終止(例如,它是最后一個(gè)非守護(hù)線(xiàn)程)而需要終止JVM。線(xiàn)程終止時(shí),將釋放本機(jī)線(xiàn)程和Java線(xiàn)程的所有資源。Java線(xiàn)程終止后,將回收本機(jī)線(xiàn)程。因此,操作系統(tǒng)負(fù)責(zé)調(diào)度所有線(xiàn)程并將其分配給任何可用的CPU。
5.程序計(jì)數(shù)器(Program Counter Register)
? 對(duì)于每個(gè)JVM線(xiàn)程,當(dāng)線(xiàn)程啟動(dòng)時(shí),將創(chuàng)建一個(gè)單獨(dú)的PC(程序計(jì)數(shù)器)寄存器,以保存當(dāng)前正在執(zhí)行的指令的地址(“方法”區(qū)域中的內(nèi)存地址)。如果當(dāng)前方法是本地方法,則PC是未定義的。執(zhí)行完成后,PC寄存器將更新為下一條指令的地址。
執(zhí)行引擎(Execution Engine)
字節(jié)碼的實(shí)際執(zhí)行在這里進(jìn)行。執(zhí)行引擎通過(guò)讀取分配給以上運(yùn)行時(shí)數(shù)據(jù)區(qū)域的數(shù)據(jù)逐行執(zhí)行字節(jié)碼中的指令。
1.解釋?zhuān)↖nterpreter)
? 解釋器解釋字節(jié)碼并一對(duì)一執(zhí)行指令。因此,它可以快速解釋一個(gè)字節(jié)碼行,但是執(zhí)行解釋后的結(jié)果是一項(xiàng)較慢的任務(wù)。缺點(diǎn)是,當(dāng)多次調(diào)用一個(gè)方法時(shí),每次都需要新的解釋和較慢的執(zhí)行。
2.即時(shí)編譯(Just-In-Time Compiler)
? 如果只有解釋器可用,當(dāng)一個(gè)方法被多次調(diào)用時(shí),每次都會(huì)進(jìn)行解釋?zhuān)绻忉屘幚碛行?,這將是多余的操作。使用JIT編譯器已經(jīng)可以做到這一點(diǎn)。首先,它將整個(gè)字節(jié)碼編譯為本機(jī)代碼(機(jī)器代碼)。然后,對(duì)于重復(fù)的方法調(diào)用,它直接提供了本機(jī)代碼,使用本機(jī)代碼的執(zhí)行比單步解釋指令要快得多。本機(jī)代碼存儲(chǔ)在緩存中,因此可以更快地執(zhí)行編譯后的代碼。
? 但是,即使對(duì)于JIT編譯器,編譯所花費(fèi)的時(shí)間也要比解釋器所花費(fèi)的時(shí)間更多。對(duì)于僅執(zhí)行一次的代碼段,最好對(duì)其進(jìn)行解釋而不是進(jìn)行編譯。同樣,本機(jī)代碼存儲(chǔ)在高速緩存中,這是一種昂貴的資源。在這種情況下,JIT編譯器會(huì)在內(nèi)部檢查每個(gè)方法調(diào)用的頻率,并僅在所選方法發(fā)生超過(guò)特定時(shí)間級(jí)別時(shí)才決定編譯每個(gè)方法。自適應(yīng)編譯的想法已在Oracle Hotspot VM中使用。
? 當(dāng)JVM供應(yīng)商引入性能優(yōu)化時(shí),執(zhí)行引擎有資格成為關(guān)鍵子系統(tǒng)。在這些工作中,以下4個(gè)組件可以大大提高其性能。
- 中間代碼生成器生成中間代碼
- 代碼優(yōu)化器負(fù)責(zé)優(yōu)化上面生成的中間代碼
- 目標(biāo)代碼生成器負(fù)責(zé)生成本機(jī)代碼(即機(jī)器代碼)
- Profiler是一個(gè)特殊的組件,負(fù)責(zé)查找性能瓶頸(也稱(chēng)為熱點(diǎn))(例如,多次調(diào)用一種方法的實(shí)例)
Oracle Hotspot虛擬機(jī)的優(yōu)化方法
Oracle有兩種流行的JIT編譯器模型。Hotspot Compiler來(lái)實(shí)現(xiàn)其標(biāo)準(zhǔn)Java VM的兩種實(shí)現(xiàn)。通過(guò)分析,它可以確定最需要JIT編譯的熱點(diǎn),然后將代碼的那些性能關(guān)鍵部分編譯為本機(jī)代碼。隨著時(shí)間的流逝,如果不再頻繁調(diào)用這種已編譯方法,它將把該方法標(biāo)識(shí)為不再是熱點(diǎn),并迅速?gòu)木彺嬷袆h除本機(jī)代碼并開(kāi)始以解釋器模式運(yùn)行。這種方法可以提高性能,同時(shí)避免不必要地編譯很少使用的代碼。此外,Hotspot Compiler可以即時(shí)確定使用lining等技術(shù)來(lái)優(yōu)化已編譯代碼的最佳方式。編譯器執(zhí)行的運(yùn)行時(shí)分析使它可以消除在確定哪些優(yōu)化將產(chǎn)生最大性能收益方面的猜測(cè)。
? 這些虛擬機(jī)運(yùn)行時(shí)使用相同的(解釋器,內(nèi)存,線(xiàn)程),但是將自定義構(gòu)建JIT編譯器的實(shí)現(xiàn),如下所述:
? Oracle Java Hotspot Client VM是Oracle JDK和JRE的默認(rèn)VM技術(shù)。它通過(guò)減少應(yīng)用程序啟動(dòng)時(shí)間和內(nèi)存占用量而在客戶(hù)端環(huán)境中運(yùn)行應(yīng)用程序時(shí)進(jìn)行了優(yōu)化,以實(shí)現(xiàn)最佳性能。
? Oracle Java Hotspot Server VM旨在為在服務(wù)器環(huán)境中運(yùn)行的應(yīng)用程序提供最高的程序執(zhí)行速度。此處使用的JIT編譯器稱(chēng)為“高級(jí)動(dòng)態(tài)優(yōu)化編譯器”,它使用更復(fù)雜和多樣化的性能優(yōu)化技術(shù)。通過(guò)使用服務(wù)器命令行選項(xiàng)(例如,java服務(wù)器MyApp)來(lái)調(diào)用Java HotSpot Server VM。
? Oracle的Java Hotspot技術(shù)以其快速的內(nèi)存分配,快速高效的GC以及易于在大型共享內(nèi)存多處理器服務(wù)器中擴(kuò)展的線(xiàn)程處理能力而聞名。
IBM AOT(提前)編譯
? 這里的特色是這些JVM共享通過(guò)共享緩存編譯的本機(jī)代碼,因此,已經(jīng)通過(guò)AOT編譯器編譯的代碼可以由另一個(gè)JVM使用,而無(wú)需編譯。另外,IBM JVM通過(guò)使用AOT編譯器將代碼預(yù)編譯為JXE(Java可執(zhí)行文件)文件格式,提供了一種快速的執(zhí)行方式。
3.垃圾收集(Garbage Collector, GC)
? 只要引用了一個(gè)對(duì)象,JVM就會(huì)認(rèn)為它是活動(dòng)的。一旦不再引用對(duì)象,應(yīng)用程序代碼無(wú)法訪問(wèn)該對(duì)象,則垃圾收集器將其刪除并回收未使用的內(nèi)存。通常,垃圾回收是在后臺(tái)進(jìn)行的,但是我們可以通過(guò)調(diào)用System.gc()方法來(lái)觸發(fā)垃圾回收(同樣無(wú)法保證執(zhí)行。因此,請(qǐng)調(diào)用Thread.sleep(1000)并等待GC完成)。
Java本地接口(JAVA Native Interface,JNI)
? 該接口用于與執(zhí)行所需的本地方法庫(kù)進(jìn)行交互,并提供此類(lèi)本地庫(kù)的功能(通常用C / C ++編寫(xiě))。這使JVM可以調(diào)用C / C ++庫(kù),并可以由特定于硬件的C / C ++庫(kù)調(diào)用。
本地方法庫(kù)(Native Method Libraries)
? 這是執(zhí)行引擎所需的C / C ++本地庫(kù)的集合,可以通過(guò)提供的本地接口進(jìn)行訪問(wèn)。
JVM線(xiàn)程(JVM Thread)
? 實(shí)際上,為了執(zhí)行我們前面討論的每個(gè)任務(wù),JVM同時(shí)運(yùn)行多個(gè)線(xiàn)程。這些線(xiàn)程中的一些帶有編程邏輯,是由程序創(chuàng)建的(應(yīng)用程序線(xiàn)程),而其余的則是由JVM本身創(chuàng)建的,以承擔(dān)系統(tǒng)中的后臺(tái)任務(wù)(系統(tǒng)線(xiàn)程)。
主應(yīng)用程序線(xiàn)程是作為調(diào)用公共靜態(tài)void main(String [])的一部分而創(chuàng)建的主線(xiàn)程,而所有其他應(yīng)用程序線(xiàn)程都是由該主線(xiàn)程創(chuàng)建的。應(yīng)用程序線(xiàn)程執(zhí)行諸如執(zhí)行以main()方法開(kāi)頭的指令,在Heap區(qū)域中創(chuàng)建對(duì)象(如果它在任何方法邏輯中找到新關(guān)鍵字)等任務(wù)。
主要系統(tǒng)線(xiàn)程
- 編譯器線(xiàn)程:在運(yùn)行時(shí),這些線(xiàn)程將字節(jié)碼編譯為本地代碼。
- GC線(xiàn)程:所有與GC相關(guān)的活動(dòng)均由這些線(xiàn)程執(zhí)行。
- 定期任務(wù)線(xiàn)程:用于調(diào)度定期操作執(zhí)行的計(jì)時(shí)器事件(即中斷)由該線(xiàn)程執(zhí)行。
- 信號(hào)調(diào)度程序線(xiàn)程:此線(xiàn)程接收發(fā)送到JVM進(jìn)程的信號(hào),并通過(guò)調(diào)用適當(dāng)?shù)腏VM方法在JVM內(nèi)處理它們。
- VM線(xiàn)程:作為前提條件,某些操作需要JVM到達(dá)安全點(diǎn),在該點(diǎn)不再進(jìn)行對(duì)Heap區(qū)域的修改。這種情況的示例是“世界停止”垃圾回收,線(xiàn)程堆棧轉(zhuǎn)儲(chǔ),線(xiàn)程掛起和有偏向的鎖吊銷(xiāo)。這些操作可以在稱(chēng)為VM線(xiàn)程的特殊線(xiàn)程上執(zhí)行。
一些了解的點(diǎn)(Some Pointers for Understanding)
- Java被認(rèn)為是解釋語(yǔ)言和編譯語(yǔ)言。
- 根據(jù)設(shè)計(jì),由于動(dòng)態(tài)鏈接和運(yùn)行時(shí)解釋?zhuān)琂ava速度很慢。
- JIT編譯器通過(guò)保留本機(jī)代碼而不是字節(jié)碼來(lái)補(bǔ)償解釋器重復(fù)操作的缺點(diǎn)。
- 最新的Java版本解決了其原始體系結(jié)構(gòu)中的性能瓶頸。
- JVM只是一個(gè)規(guī)范。供應(yīng)商可以在實(shí)施過(guò)程中自由定制,創(chuàng)新和改善其性能。
