新聞中心
JVM簡(jiǎn)介
JVM(Java Virtual Machine,Java虛擬機(jī))是Java語(yǔ)言的核心,是一個(gè)用于解釋Java字節(jié)碼的虛擬計(jì)算機(jī)。它可以在運(yùn)行Java程序時(shí)自動(dòng)管理內(nèi)存、處理異常等。Java程序員不需要關(guān)心底層硬件和操作系統(tǒng)的細(xì)節(jié),只需要編寫(xiě)符合Java語(yǔ)法規(guī)范的代碼,就可以實(shí)現(xiàn)跨平臺(tái)的編程。

橋東網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、響應(yīng)式網(wǎng)站等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。成都創(chuàng)新互聯(lián)自2013年創(chuàng)立以來(lái)到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專(zhuān)注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)。
當(dāng)我們編寫(xiě)Java程序時(shí),Java源代碼會(huì)被編譯成為Java字節(jié)碼( .java 文件被編譯成 .class 文件)。這些字節(jié)碼可以在任何安裝了Java虛擬機(jī)的平臺(tái)上運(yùn)行。JVM在執(zhí)行Java字節(jié)碼時(shí),將其轉(zhuǎn)換成特定于底層CPU和操作系統(tǒng)的機(jī)器代碼。
運(yùn)行時(shí)數(shù)據(jù)區(qū)簡(jiǎn)介
為了執(zhí)行字節(jié)碼,JVM在內(nèi)存中定義了一系列的數(shù)據(jù)區(qū),用于在運(yùn)行時(shí)存儲(chǔ)各類(lèi)數(shù)據(jù),即運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Areas)。理解這些數(shù)據(jù)區(qū)及其作用,是掌握J(rèn)ava性能調(diào)優(yōu)和錯(cuò)誤排查的關(guān)鍵。
JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)是 Java 虛擬機(jī)在執(zhí)行 Java 程序時(shí)用于數(shù)據(jù)存儲(chǔ)的內(nèi)存區(qū)域,這些區(qū)域各司其職,確保了 Java 程序的正確執(zhí)行。JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)主要分為五個(gè)部分:程序計(jì)數(shù)器(Program Counter Register)、虛擬機(jī)棧(VM Stack)、本地方法棧(Native Method Stack)、堆(Heap)、方法區(qū)(Method Area)。JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)在程序運(yùn)行時(shí)動(dòng)態(tài)地分配和釋放內(nèi)存,內(nèi)存管理由JVM自動(dòng)完成。不同的數(shù)據(jù)區(qū)域有不同的內(nèi)存管理機(jī)制和垃圾回收算法,以保證程序運(yùn)行的效率和穩(wěn)定性。
其中程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧屬于線(xiàn)程私有區(qū)域,跟隨線(xiàn)程的啟動(dòng)和結(jié)束而建立和銷(xiāo)毀。堆和方法區(qū)是線(xiàn)程共享區(qū)域,跟隨虛擬機(jī)進(jìn)程的啟動(dòng)而存在。
程序計(jì)數(shù)器(Program Counter Register) 是一塊較小的內(nèi)存空間,作用是指示當(dāng)前線(xiàn)程正在執(zhí)行的 JVM 字節(jié)碼指令地址。
虛擬機(jī)棧(VM Stack) 存放的是一些基本類(lèi)型的變量(如int, long)和對(duì)象引用。Java 方法執(zhí)行的內(nèi)存模型是以棧幀(Stack Frame)為基礎(chǔ)的,每個(gè)方法在執(zhí)行的時(shí)候都會(huì)創(chuàng)建一個(gè)棧幀,棧幀中存放了局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。
本地方法棧(Native Method Stack) 與虛擬機(jī)棧類(lèi)似,其主要服務(wù)于 JVM 使用到的 Native 方法。
堆區(qū)(Heap) 是 JVM 所管理的最大一塊內(nèi)存空間,主要用于存放所有線(xiàn)程共享的 Java 對(duì)象實(shí)例。這也是垃圾回收器主要活動(dòng)區(qū)域。
方法區(qū)(Method Area) 是用來(lái)存儲(chǔ)加載的類(lèi)信息、常量、靜態(tài)變量等數(shù)據(jù)的。這個(gè)區(qū)域是線(xiàn)程共享的。
1. 程序計(jì)數(shù)器
程序計(jì)數(shù)器(Program Counter Register)是線(xiàn)程私有區(qū)域,生命周期與線(xiàn)程一致,也是 JVM 內(nèi)存中唯一一個(gè)沒(méi)有任何 OutOfMemoryError 的區(qū)域。
程序計(jì)數(shù)器的作用是記錄當(dāng)前線(xiàn)程正在執(zhí)行的指令地址,換句話(huà)說(shuō),它指向了下一條將要被執(zhí)行的 JVM 字節(jié)碼指令。在 JVM 的概念模型中,字節(jié)碼解釋器工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令。
當(dāng)線(xiàn)程執(zhí)行的是 Java 方法時(shí),這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;如果正在執(zhí)行的是 Native 方法,這個(gè)計(jì)數(shù)器的值則為空(Undefined)。
程序計(jì)數(shù)器對(duì)于現(xiàn)代多線(xiàn)程而言至關(guān)重要,因?yàn)樵?CPU 切換各個(gè)線(xiàn)程時(shí),需要將各個(gè)線(xiàn)程的程序計(jì)數(shù)器記錄下來(lái),以便在下一次切換回這個(gè)線(xiàn)程時(shí),能知道該從哪里繼續(xù)執(zhí)行。
總結(jié):
- 程序計(jì)數(shù)器是一塊很小的內(nèi)存空間,也是運(yùn)行速度最快的存儲(chǔ)區(qū)域。
- 在 JVM 規(guī)范中,每個(gè)線(xiàn)程都有它自己的程序計(jì)數(shù)器,是線(xiàn)程私有的,生命周期與線(xiàn)程的生命周期一致。
- 如果當(dāng)前線(xiàn)程正在執(zhí)行的是 Java 方法,程序計(jì)數(shù)器記錄的是 JVM 字節(jié)碼指令地址,如果是執(zhí)行 native 方法,則是未指定值(undefined)
- 它是程序控制流的指示器,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線(xiàn)程恢復(fù)等基礎(chǔ)功能都需要依賴(lài)這個(gè)計(jì)數(shù)器來(lái)完成
- 字節(jié)碼解釋器工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令
- 它是唯一一個(gè)在 JVM 規(guī)范中沒(méi)有規(guī)定任何 OutOfMemoryError 情況的區(qū)域
2. 虛擬機(jī)棧
與程序計(jì)數(shù)器一樣,Java虛擬機(jī)棧(Java Virtual Machine Stacks)也是線(xiàn)程私有的,生命周期與線(xiàn)程相同。描述的是Java方法執(zhí)行的內(nèi)存模型。
在 JVM 中,每當(dāng)一個(gè)新的線(xiàn)程被創(chuàng)建,都會(huì)創(chuàng)建一個(gè)與之關(guān)聯(lián)的私有 JVM 棧。這個(gè)棧會(huì)隨著線(xiàn)程的運(yùn)行而進(jìn)行入棧(push)和出棧(pop)操作。它主要用于存儲(chǔ)局部變量、操作數(shù)堆棧以及方法調(diào)用的情況。
JVM 棧是由一系列棧幀(Stack Frame)組成的。每當(dāng)一個(gè)方法被調(diào)用,一個(gè)新的棧幀就會(huì)被壓入棧中,每當(dāng)一個(gè)方法調(diào)用結(jié)束,一個(gè)棧幀就會(huì)被彈出棧。每個(gè)棧幀中都包含了局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接和方法返回地址等信息。
局部變量表主要存放了編譯期可知的各種基本數(shù)據(jù)類(lèi)型(boolean、byte、char、short、int、float、long、double)、對(duì)象引用(reference 類(lèi)型,它不等同于指針,可能是一個(gè)指向?qū)ο笃鹗嫉刂返囊弥羔?,也可能是指向一個(gè)代表對(duì)象的句柄或者其他與此對(duì)象相關(guān)的位置)和 returnAddress 類(lèi)型(指向了一條字節(jié)碼指令的地址)。
操作數(shù)棧則是在執(zhí)行字節(jié)碼指令時(shí)用到的臨時(shí)存儲(chǔ)區(qū),比如在進(jìn)行算數(shù)運(yùn)算時(shí),操作數(shù)棧就會(huì)用來(lái)存放操作數(shù)和接收結(jié)果。
Java虛擬機(jī)棧可能會(huì)拋出以下異常:
- 如果線(xiàn)程請(qǐng)求的棧深度大于 JVM 所允許的深度,將拋出 StackOverflowError。
- 如果 JVM 棧可以動(dòng)態(tài)擴(kuò)展,當(dāng)擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存,會(huì)拋出 OutOfMemoryError。
3. 本地方法棧
本地方法棧(Native Method Stack)也是線(xiàn)程私有,生命周期與線(xiàn)程相同。作用是與虛擬機(jī)棧類(lèi)似,虛擬機(jī)棧是為Java 方法服務(wù)的,而本地方法棧是為 Native 方法服務(wù)的。
和虛擬機(jī)棧一樣,本地方法棧的大小可以是固定的也可以是動(dòng)態(tài)的。如果是固定的,當(dāng)線(xiàn)程請(qǐng)求的棧深度超過(guò)最大深度時(shí),會(huì)拋出 StackOverflowError。如果是動(dòng)態(tài)的,并且在嘗試擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存,會(huì)拋出 OutOfMemoryError。
4. 堆
堆(Heap)是 JVM 所管理的最大一塊內(nèi)存空間,也是所有線(xiàn)程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。堆主要用于存儲(chǔ)對(duì)象實(shí)例和數(shù)組,這也是 Java 垃圾回收器主要活動(dòng)的區(qū)域。
在物理上,堆區(qū)可以處于分散的內(nèi)存空間中,但在邏輯上它被視為連續(xù)的。堆區(qū)在 JVM 啟動(dòng)時(shí)創(chuàng)建,如果堆區(qū)的空間不足,將會(huì)拋出 OutOfMemoryError。
堆分為新生代(Young Generation)和老年代(Old Generation)。新生代又分為 Eden 區(qū)、From Survivor 區(qū)(簡(jiǎn)稱(chēng) S0)、 To Survivor 區(qū)(簡(jiǎn)稱(chēng) S1)。劃分這么多區(qū)域的目的是為了更好地回收內(nèi)存,或者更快地分配內(nèi)存。
新生代中各個(gè)區(qū)域的內(nèi)存占比分別是,Eden : S0 : S1 = 8 : 1 : 1
新創(chuàng)建的對(duì)象優(yōu)先在 Eden 區(qū)進(jìn)行分配。當(dāng) Eden 區(qū)滿(mǎn)時(shí),會(huì)觸發(fā)一次 Minor GC(新生代垃圾回收,也叫 Young GC),將仍然存活的對(duì)象從 Eden 區(qū)和 S0 區(qū)移動(dòng)到 S1 區(qū),下次 Minor GC 處理情況類(lèi)似,把存活的對(duì)象從 Eden 區(qū)和 S1 區(qū)移動(dòng)到 S0 區(qū)。當(dāng) Survivor 區(qū)也滿(mǎn)了,還存活的對(duì)象會(huì)被移動(dòng)到老年代。如果老年代也滿(mǎn)了,將會(huì)觸發(fā) Major GC(老年代垃圾回收,也叫 Old GC)。當(dāng)老年代滿(mǎn)了,也可能觸發(fā) Full GC,F(xiàn)ull GC 會(huì)對(duì)整個(gè)堆內(nèi)存進(jìn)行垃圾回收,包含新生代、老年代和方法區(qū)。Full GC 會(huì)導(dǎo)致較長(zhǎng)的停頓時(shí)間,并且會(huì)消耗大量的系統(tǒng)資源。
5. 方法區(qū)
方法區(qū)(Method Area)與堆一樣,是所有線(xiàn)程共享的內(nèi)存區(qū)域,用于存儲(chǔ)已被虛擬機(jī)加載的類(lèi)信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。
方法區(qū)只是 JVM 規(guī)范中定義的一個(gè)概念,針對(duì) Hotspot 虛擬機(jī),JDK8 之前使用永久代(Permanent Generation,簡(jiǎn)稱(chēng) PermGen)實(shí)現(xiàn),JDK8 使用元空間(Metaspace)實(shí)現(xiàn)。
JDK8 之前可以通過(guò) -XX:PermSize 和 -XX:MaxPermSize 來(lái)設(shè)置永久代大小,JDK8 之后,使用元空間替換了永久代,改為通過(guò) -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 來(lái)設(shè)置元空間大小。
運(yùn)行時(shí)常量池
運(yùn)行時(shí)常量池(Runtime Constant Pool)是方法區(qū)中的一部分,用于存儲(chǔ)編譯期間生成的各種字面量和符號(hào)引用。在Java程序運(yùn)行時(shí),JVM將編譯期生成的class文件中的常量池內(nèi)容讀取到運(yùn)行時(shí)常量池中。
運(yùn)行時(shí)常量池存儲(chǔ)了類(lèi)和接口中的常量,包括字符串字面量、被聲明為final的常量值等。它還存儲(chǔ)了類(lèi)和接口中的符號(hào)引用,如類(lèi)和接口、字段和方法的引用等。
在JVM中,運(yùn)行時(shí)常量池是線(xiàn)程安全的。每個(gè)線(xiàn)程都有一個(gè)自己的線(xiàn)程棧,其中包含了局部變量表,而這些局部變量表中所引用的對(duì)象都位于堆中。當(dāng)一個(gè)線(xiàn)程需要引用運(yùn)行時(shí)常量池中的常量時(shí),JVM會(huì)先將常量值從運(yùn)行時(shí)常量池中復(fù)制到線(xiàn)程棧的局部變量表中,然后再進(jìn)行引用。
需要注意的是,在JDK8中,運(yùn)行時(shí)常量池已經(jīng)被移動(dòng)到元空間(Metaspace)中。元空間是在本地內(nèi)存中分配的,與JVM的堆內(nèi)存是分離的,因此不會(huì)受到Java堆大小的限制。
分享題目:面試必問(wèn),JVM內(nèi)存模型掃盲
鏈接分享:http://fisionsoft.com.cn/article/djoecgd.html


咨詢(xún)
建站咨詢(xún)
