JVM解毒——JVM與Java體系結(jié)構(gòu)

你是否也遇到過(guò)這些問(wèn)題?

運(yùn)行線上系統(tǒng)突然卡死,系統(tǒng)無(wú)法訪問(wèn),甚至直接OOM

想解決線上JVM GC問(wèn)題,但卻無(wú)從下手

新項(xiàng)目上線,對(duì)各種JVM參數(shù)設(shè)置一臉懵逼,直接默認(rèn),然后就JJ了

每次面試都要重新背一遍JVM的一些原理概念性東西

這段廣告語(yǔ)寫(xiě)的好,趁著在家辦公學(xué)習(xí)下JVM,先列出整體知識(shí)點(diǎn)

Java開(kāi)發(fā)都知道JVM是Java虛擬機(jī),上學(xué)時(shí)還用過(guò)的VM也叫虛擬機(jī),先比較一波

虛擬機(jī)與Java虛擬機(jī)

所謂虛擬機(jī)(Virtual Machine),就是一臺(tái)虛擬的計(jì)算機(jī)。它是一款軟件,用來(lái)執(zhí)行一系列虛擬計(jì)算機(jī)指令。大體上,虛擬機(jī)可以分為系統(tǒng)虛擬機(jī)程序虛擬機(jī)

Visaual Box,VMware就屬于系統(tǒng)虛擬機(jī),它們完全是對(duì)物理計(jì)算機(jī)的仿真,提供了一個(gè)可運(yùn)行完整操作系統(tǒng)的軟件平臺(tái)

程序虛擬機(jī)的典型代表就是Java虛擬機(jī),它專(zhuān)門(mén)為執(zhí)行單個(gè)計(jì)算機(jī)程序而設(shè)計(jì),在Java虛擬機(jī)中執(zhí)行的指令我們稱(chēng)為Java字節(jié)碼指令

JVM 是什么

JVM 是 Java Virtual Machine(Java虛擬機(jī))的縮寫(xiě),JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)的計(jì)算機(jī),是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的。

Java虛擬機(jī)是二進(jìn)制字節(jié)碼的運(yùn)行環(huán)境,負(fù)責(zé)裝載字節(jié)碼到其內(nèi)部,解釋/編譯為對(duì)應(yīng)平臺(tái)的機(jī)器指令執(zhí)行。每一條Java指令,Java虛擬機(jī)規(guī)范中都有詳細(xì)定義,如怎么取操作數(shù),怎么處理操作數(shù),處理結(jié)果放在哪里。

特點(diǎn)

一次編譯,到處運(yùn)次(跨平臺(tái))

自動(dòng)內(nèi)存管理

自動(dòng)垃圾回收功能

字節(jié)碼

我們平時(shí)所說(shuō)的java字節(jié)碼,指的是用java語(yǔ)言編寫(xiě)的字節(jié)碼,準(zhǔn)確的說(shuō)任何能在jvm平臺(tái)上執(zhí)行的字節(jié)碼格式都是一樣的,所以應(yīng)該統(tǒng)稱(chēng)為jvm字節(jié)碼。

不同的編譯器可以編譯出相同的字節(jié)碼文件,字節(jié)碼文件也可以在不同的jvm上運(yùn)行。

Java虛擬機(jī)與Java語(yǔ)言沒(méi)有必然的聯(lián)系,它只與特定的二進(jìn)制文件格式——Class文件格式關(guān)聯(lián),Class文件中包含了Java虛擬機(jī)指令集(或者稱(chēng)為字節(jié)碼、Bytecodes)和符號(hào)集,還有一些其他輔助信息。

Java代碼執(zhí)行過(guò)程

JVM的位置

JVM是運(yùn)行在操作系統(tǒng)之上的,它與硬件沒(méi)有直接的交互。

JDK(Java Development Kit) 是 Java 語(yǔ)言的軟件開(kāi)發(fā)工具包(SDK)。JDK 物理存在,是 Java Language、Tools、JRE 和 JVM 的一個(gè)集合。

JVM整體結(jié)構(gòu)

JVM的架構(gòu)模型

Java編譯器輸入的指令流基本上是一種基于棧的指令集架構(gòu),另外一種指令集架構(gòu)則是基于寄存器的指令集架構(gòu)。

兩種架構(gòu)之間的區(qū)別:

基于棧式架構(gòu)的特點(diǎn)設(shè)計(jì)和實(shí)現(xiàn)更簡(jiǎn)單,適用于資源受限的系統(tǒng);避開(kāi)了寄存器的分配難題,使用零地址指令方式分配;指令流中的指令大部分是零地址指令,其執(zhí)行過(guò)程依賴(lài)于操作棧。指令集更小,編譯器容易實(shí)現(xiàn);不需要硬件支持,可移植性更好,更好實(shí)現(xiàn)跨平臺(tái)

基于寄存器架構(gòu)的特點(diǎn)典型的應(yīng)用是X86的二進(jìn)制指令集:比如傳統(tǒng)的PC以及Android的Davlik虛擬機(jī);指令集架構(gòu)則完全依賴(lài)硬件,可移植性差;性能優(yōu)秀和執(zhí)行更高效;花費(fèi)更少的指令去完成一項(xiàng)操作;大部分情況下,基于寄存器架構(gòu)的指令集往往都以一地址指令、二地址指令和三地址指令為主,而基于棧式架構(gòu)的指令集卻是以零地址指令為主

由于跨平臺(tái)性的設(shè)計(jì),Java的指令都是根據(jù)棧來(lái)設(shè)計(jì)的。不同平臺(tái)CPU架構(gòu)不同,所以不能設(shè)計(jì)為基于寄存器的,優(yōu)點(diǎn)是跨平臺(tái),指令集小,編譯器容易實(shí)現(xiàn),缺點(diǎn)是性能下降,實(shí)現(xiàn)同樣的功能需要更多的指令。

分析基于棧式架構(gòu)的JVM代碼執(zhí)行過(guò)程

進(jìn)入class文件所在目錄,執(zhí)行javap -v xx.class反解析(或者通過(guò)IDEA插件Jclasslib直接查看),可以看到當(dāng)前類(lèi)對(duì)應(yīng)的code區(qū)(匯編指令)、本地變量表、異常表和代碼行偏移量映射表、常量池等信息。

以上圖中的 1+2 為例說(shuō)明:

Classfile /Users/starfish/workspace/myCode/starfish-learning/starfish-learn/target/classes/priv/starfish/jvm/JVM1.class

Last modified 2020-2-7; size 487 bytes

MD5checksum1a9653128b55585b2745270d13b17aaf

Compiledfrom"JVM1.java"

publicclasspriv.starfish.jvm.JVM1

SourceFile:"JVM1.java"

minorversion:0

majorversion:52

flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

#1 = Methodref? ? ? ? ? #3.#22? ? ? ? //? java/lang/Object."<init>":()V

? #2 = Class? ? ? ? ? ? ? #23? ? ? ? ? ? //? priv/starfish/jvm/JVM1

? #3 = Class? ? ? ? ? ? ? #24? ? ? ? ? ? //? java/lang/Object

? #4 = Utf8? ? ? ? ? ? ? <init>

? #5 = Utf8? ? ? ? ? ? ? ()V

? #6 = Utf8? ? ? ? ? ? ? Code

? #7 = Utf8? ? ? ? ? ? ? LineNumberTable

? #8 = Utf8? ? ? ? ? ? ? LocalVariableTable

? #9 = Utf8? ? ? ? ? ? ? this

? #10 = Utf8? ? ? ? ? ? ? Lpriv/starfish/jvm/JVM1;

? #11 = Utf8? ? ? ? ? ? ? main

? #12 = Utf8? ? ? ? ? ? ? ([Ljava/lang/String;)V

? #13 = Utf8? ? ? ? ? ? ? args

? #14 = Utf8? ? ? ? ? ? ? [Ljava/lang/String;

? #15 = Utf8? ? ? ? ? ? ? i

? #16 = Utf8? ? ? ? ? ? ? I

? #17 = Utf8? ? ? ? ? ? ? j

? #18 = Utf8? ? ? ? ? ? ? k

? #19 = Utf8? ? ? ? ? ? ? MethodParameters

? #20 = Utf8? ? ? ? ? ? ? SourceFile

? #21 = Utf8? ? ? ? ? ? ? JVM1.java

? #22 = NameAndType? ? ? ? #4:#5? ? ? ? ? //? "<init>":()V

? #23 = Utf8? ? ? ? ? ? ? priv/starfish/jvm/JVM1

? #24 = Utf8? ? ? ? ? ? ? java/lang/Object

{

? public priv.starfish.jvm.JVM1();

? ? flags: ACC_PUBLIC

? ? Code:

? ? ? stack=1, locals=1, args_size=1

? ? ? ? 0: aload_0? ? ?

? ? ? ? 1: invokespecial #1? ? ? ? ? ? ? ? ? // Method java/lang/Object."<init>":()V

? ? ? ? 4: return? ? ? ?

? ? ? LineNumberTable:

? ? ? ? line 3: 0

? ? ? LocalVariableTable:

? ? ? ? Start? Length? Slot? Name? Signature

? ? ? ? ? ? ? 0? ? ? 5? ? 0? this? Lpriv/starfish/jvm/JVM1;

? public static void main(java.lang.String[]);

? ? flags: ACC_PUBLIC, ACC_STATIC

? ? Code:

? ? ? stack=2, locals=4, args_size=1

? ? ? ? 0: iconst_1? ? ? //冒號(hào)前的數(shù)字表示程序計(jì)數(shù)器的數(shù),常量1入棧

? ? ? ? 1: istore_1? ? ? //保存到1的操作數(shù)棧中,這里的1表示操作數(shù)棧的索引位置

? ? ? ? 2: iconst_2? ? ?

? ? ? ? 3: istore_2? ? ?

? ? ? ? 4: iload_1? ? ? //加載

? ? ? ? 5: iload_2? ? ?

? ? ? ? 6: iadd? ? ? ? ? //常量出棧,求和

? ? ? ? 7: istore_3? ? ? //存儲(chǔ)到索引為3的操作數(shù)棧

? ? ? ? 8: return? ? ? ?

? ? ? LineNumberTable:

? ? ? ? line 6: 0

? ? ? ? line 7: 2

? ? ? ? line 8: 4

? ? ? ? line 9: 8

? ? ? LocalVariableTable:

? ? ? ? Start? Length? Slot? Name? Signature

? ? ? ? ? ? ? 0? ? ? 9? ? 0? args? [Ljava/lang/String;

? ? ? ? ? ? ? 2? ? ? 7? ? 1? ? i? I

? ? ? ? ? ? ? 4? ? ? 5? ? 2? ? j? I

? ? ? ? ? ? ? 8? ? ? 1? ? 3? ? k? I

? ? ? MethodParameters: length = 0x5

? ? ? 01 00 0D 00 00

}

JVM生命周期

虛擬機(jī)的啟動(dòng)

Java虛擬機(jī)的啟動(dòng)是通過(guò)引導(dǎo)類(lèi)加載器(Bootstrap Class Loader)創(chuàng)建一個(gè)初始類(lèi)(initial class)來(lái)完成的,這個(gè)類(lèi)是由虛擬機(jī)的具體實(shí)現(xiàn)指定的。

虛擬機(jī)的執(zhí)行

一個(gè)運(yùn)行中的Java虛擬機(jī)有著一個(gè)清晰的任務(wù):執(zhí)行Java程序

程序開(kāi)始執(zhí)行時(shí)它才運(yùn)行,程序結(jié)束時(shí)它就停止

執(zhí)行一個(gè)所謂的Java程序的時(shí)候,真正執(zhí)行的是一個(gè)叫做Java虛擬機(jī)的進(jìn)程

你在同一臺(tái)機(jī)器上運(yùn)行三個(gè)程序,就會(huì)有三個(gè)運(yùn)行中的Java虛擬機(jī)。 Java虛擬機(jī)總是開(kāi)始于一個(gè)main()方法,這個(gè)方法必須是公有、返回void、只接受一個(gè)字符串?dāng)?shù)組。在程序執(zhí)行時(shí),你必須給Java虛擬機(jī)指明這個(gè)包含main()方法的類(lèi)名。

虛擬機(jī)的退出

有以下幾種情況:

程序正常執(zhí)行結(jié)束

程序在執(zhí)行過(guò)程中遇到了異?;蝈e(cuò)誤而異常終止

由于操作系統(tǒng)出現(xiàn)錯(cuò)誤而導(dǎo)致Java虛擬機(jī)進(jìn)程終止

某線程調(diào)用Runtime類(lèi)或System類(lèi)的exit方法,或Runtime類(lèi)的halt方法,并且Java安全管理器也允許這次exit或halt操作

除此之外,JNI(Java Native Interface)規(guī)范描述了用JNI Invocation API來(lái)加載或卸載Java虛擬機(jī)時(shí),Java虛擬機(jī)的退出情況

Java和JVM規(guī)范

Java Language and Virtual Machine Specifications

JVM發(fā)展歷程

JDK 版本升級(jí)不僅僅體現(xiàn)在語(yǔ)言和功能特性上,還包括了其編譯和執(zhí)行的 Java 虛擬機(jī)的升級(jí)。

1990年,在Sun計(jì)算機(jī)公司中,由Patrick Naughton、MikeSheridan及James Gosling領(lǐng)導(dǎo)的小組Green Team,開(kāi)發(fā)出的新的程序語(yǔ)言,命名為Oak,后期命名為Java

1995年,Sun正式發(fā)布Java和HotJava產(chǎn)品,Java首次公開(kāi)亮相

1996 年,JDK 1.0 發(fā)布時(shí),提供了純解釋執(zhí)行的 Java 虛擬機(jī)實(shí)現(xiàn):Sun Classic VM。

1997 年,JDK 1.1 發(fā)布時(shí),虛擬機(jī)沒(méi)有做變更,依然使用 Sun Classic VM 作為默認(rèn)的虛擬機(jī)

1998 年,JDK 1.2 發(fā)布時(shí),提供了運(yùn)行在 Solaris 平臺(tái)的 Exact VM 虛擬機(jī),但此時(shí)還是用 Sun Classic VM 作為默認(rèn)的 Java 虛擬機(jī),同時(shí)發(fā)布了JSP/Servlet、EJB規(guī)范,以及將Java分成J2EE、J2SE、J2ME

2000 年,JDK1.3 發(fā)布,默認(rèn)的 Java 虛擬機(jī)由 Sun Classic VM 改為 Sun HotSopt VM,而 Sun Classic VM 則作為備用虛擬機(jī)

2002 年,JDK 1.4 發(fā)布,Sun Classic VM 退出商用虛擬機(jī)舞臺(tái),直接使用 Sun HotSpot VM 作為默認(rèn)虛擬機(jī)一直到現(xiàn)在

2003年,Java平臺(tái)的Scala正式發(fā)布,同年Groovy也加入了Java陣營(yíng)

2004年,JDK1.5發(fā)布,同時(shí)JDK1.5改名為JDK5.0

2006年,JDK6發(fā)布,同年,Java開(kāi)源并建立了OpenJDK。順理成章,Hotspot虛擬機(jī)也成為了OpenJDK默認(rèn)虛擬機(jī)

2008年,Oracle收購(gòu)BEA,得到了JRockit虛擬機(jī)

2010年,Oracle收購(gòu)了Sun,獲得Java商標(biāo)和HotSpot虛擬機(jī)

2011年,JDK7發(fā)布,在JDK1.7u4中,正式啟用了新的垃圾回收器G1

2014年,JDK8發(fā)布,用元空間MetaSpace取代了PermGen

2017年,JDK9發(fā)布,將G1設(shè)置為默認(rèn)GC,替代CMS

Sun Classic VM

世界上第一款商用 Java 虛擬機(jī)。1996年隨著Java1.0的發(fā)布而發(fā)布,JDK1.4時(shí)完全被淘汰;

這款虛擬機(jī)內(nèi)部只提供解釋器;

如果使用JIT編譯器,就需要進(jìn)行外掛。但是一旦使用了JIT編譯器,JIT就會(huì)接管虛擬機(jī)的執(zhí)行系統(tǒng),解釋器就不再工作,解釋器和編譯器不能配合工作;

現(xiàn)在hotspot內(nèi)置了此虛擬機(jī)

Exact VM

它的執(zhí)行系統(tǒng)已經(jīng)具備了現(xiàn)代高性能虛擬機(jī)的雛形:如熱點(diǎn)探測(cè)、兩級(jí)即時(shí)編譯器、編譯器與解析器混合工作模式等;

使用準(zhǔn)確式內(nèi)存管理:虛擬機(jī)可以知道內(nèi)存中某個(gè)位置的數(shù)據(jù)具體是什么類(lèi)型;

在商業(yè)應(yīng)用上只存在了很短暫的時(shí)間就被更優(yōu)秀的 HotSpot VM 所取代

Sun HotSpot VM

它是 Sun JDK 和 OpenJDK 中所帶的虛擬機(jī),也是目前使用范圍最廣的 Java 虛擬機(jī);

繼承了 Sun 之前兩款商用虛擬機(jī)的優(yōu)點(diǎn)(如準(zhǔn)確式內(nèi)存管理),也使用了許多自己新的技術(shù)優(yōu)勢(shì),如熱點(diǎn)代碼探測(cè)技術(shù)(通過(guò)執(zhí)行計(jì)數(shù)器找出最具有編譯價(jià)值的代碼,然后通知 JIT 編譯器以方法為單位進(jìn)行編譯;

Oracle 公司分別收購(gòu)了 BEA 和 Sun,并在 JDK8 的時(shí)候,整合了 JRokit VM 和 HotSpot VM,如使用了 JRokit 的垃圾回收器與 MissionControl 服務(wù),使用了 HotSpot 的 JIT 編譯器與混合的運(yùn)行時(shí)系統(tǒng)。

BEA JRockit VM

專(zhuān)注于服務(wù)器端應(yīng)用,內(nèi)部不包含解析器實(shí)現(xiàn);

號(hào)稱(chēng)是世界上最快的JVM

IBM J9 VM

全稱(chēng):IBM Technology for Java Virtual Machine,簡(jiǎn)稱(chēng)IT4J,內(nèi)部代號(hào):J9

市場(chǎng)定位于HotSpot接近,服務(wù)器端、桌面應(yīng)用、嵌入式等多用途VM

目前是有影響力的三大商用虛擬機(jī)之一

虛擬機(jī)有很多,此外還有Azul VM、Liquid VM、Apache Harmony、TaobaoJVM、Graal VM等

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容