自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試必問(wèn),JVM內(nèi)存模型掃盲

開(kāi)發(fā) 前端
運(yùn)行時(shí)常量池(Runtime Constant Pool)是方法區(qū)中的一部分,用于存儲(chǔ)編譯期間生成的各種字面量和符號(hào)引用。在Java程序運(yùn)行時(shí),JVM將編譯期生成的class文件中的常量池內(nèi)容讀取到運(yùn)行時(shí)常量池中。

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é),只需要編寫符合Java語(yǔ)法規(guī)范的代碼,就可以實(shí)現(xiàn)跨平臺(tái)的編程。

當(dāng)我們編寫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ǔ)各類數(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ī)棧、本地方法棧屬于線程私有區(qū)域,跟隨線程的啟動(dòng)和結(jié)束而建立和銷毀。堆和方法區(qū)是線程共享區(qū)域,跟隨虛擬機(jī)進(jìn)程的啟動(dòng)而存在。

程序計(jì)數(shù)器(Program Counter Register) 是一塊較小的內(nèi)存空間,作用是指示當(dāng)前線程正在執(zhí)行的 JVM 字節(jié)碼指令地址。

虛擬機(jī)棧(VM Stack) 存放的是一些基本類型的變量(如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ī)棧類似,其主要服務(wù)于 JVM 使用到的 Native 方法。

堆區(qū)(Heap) 是 JVM 所管理的最大一塊內(nèi)存空間,主要用于存放所有線程共享的 Java 對(duì)象實(shí)例。這也是垃圾回收器主要活動(dòng)區(qū)域。

方法區(qū)(Method Area) 是用來(lái)存儲(chǔ)加載的類信息、常量、靜態(tài)變量等數(shù)據(jù)的。這個(gè)區(qū)域是線程共享的。

圖片

1. 程序計(jì)數(shù)器

程序計(jì)數(shù)器(Program Counter Register)是線程私有區(qū)域,生命周期與線程一致,也是 JVM 內(nèi)存中唯一一個(gè)沒(méi)有任何 OutOfMemoryError 的區(qū)域。

程序計(jì)數(shù)器的作用是記錄當(dāng)前線程正在執(zhí)行的指令地址,換句話說(shuō),它指向了下一條將要被執(zhí)行的 JVM 字節(jié)碼指令。在 JVM 的概念模型中,字節(jié)碼解釋器工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令。

當(dāng)線程執(zhí)行的是 Java 方法時(shí),這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;如果正在執(zhí)行的是 Native 方法,這個(gè)計(jì)數(shù)器的值則為空(Undefined)。

程序計(jì)數(shù)器對(duì)于現(xiàn)代多線程而言至關(guān)重要,因?yàn)樵?CPU 切換各個(gè)線程時(shí),需要將各個(gè)線程的程序計(jì)數(shù)器記錄下來(lái),以便在下一次切換回這個(gè)線程時(shí),能知道該從哪里繼續(xù)執(zhí)行。

總結(jié):

  • 程序計(jì)數(shù)器是一塊很小的內(nèi)存空間,也是運(yùn)行速度最快的存儲(chǔ)區(qū)域。
  • 在 JVM 規(guī)范中,每個(gè)線程都有它自己的程序計(jì)數(shù)器,是線程私有的,生命周期與線程的生命周期一致。
  • 如果當(dāng)前線程正在執(zhí)行的是 Java 方法,程序計(jì)數(shù)器記錄的是 JVM 字節(jié)碼指令地址,如果是執(zhí)行 native 方法,則是未指定值(undefined)
  • 它是程序控制流的指示器,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(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)也是線程私有的,生命周期與線程相同。描述的是Java方法執(zhí)行的內(nèi)存模型。

在 JVM 中,每當(dāng)一個(gè)新的線程被創(chuàng)建,都會(huì)創(chuàng)建一個(gè)與之關(guān)聯(lián)的私有 JVM 棧。這個(gè)棧會(huì)隨著線程的運(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ù)類型(boolean、byte、char、short、int、float、long、double)、對(duì)象引用(reference 類型,它不等同于指針,可能是一個(gè)指向?qū)ο笃鹗嫉刂返囊弥羔?,也可能是指向一個(gè)代表對(duì)象的句柄或者其他與此對(duì)象相關(guān)的位置)和 returnAddress 類型(指向了一條字節(jié)碼指令的地址)。

操作數(shù)棧則是在執(zhí)行字節(jié)碼指令時(shí)用到的臨時(shí)存儲(chǔ)區(qū),比如在進(jìn)行算數(shù)運(yùn)算時(shí),操作數(shù)棧就會(huì)用來(lái)存放操作數(shù)和接收結(jié)果。

Java虛擬機(jī)棧可能會(huì)拋出以下異常:

  1. 如果線程請(qǐng)求的棧深度大于 JVM 所允許的深度,將拋出 StackOverflowError。
  2. 如果 JVM ??梢詣?dòng)態(tài)擴(kuò)展,當(dāng)擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存,會(huì)拋出 OutOfMemoryError。

圖片

3. 本地方法棧

本地方法棧(Native Method Stack)也是線程私有,生命周期與線程相同。作用是與虛擬機(jī)棧類似,虛擬機(jī)棧是為Java 方法服務(wù)的,而本地方法棧是為 Native 方法服務(wù)的。

和虛擬機(jī)棧一樣,本地方法棧的大小可以是固定的也可以是動(dòng)態(tài)的。如果是固定的,當(dāng)線程請(qǐng)求的棧深度超過(guò)最大深度時(shí),會(huì)拋出 StackOverflowError。如果是動(dòng)態(tài)的,并且在嘗試擴(kuò)展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存,會(huì)拋出 OutOfMemoryError。

4. 堆

堆(Heap)是 JVM 所管理的最大一塊內(nèi)存空間,也是所有線程共享的一塊內(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)稱 S0)、 To Survivor 區(qū)(簡(jiǎn)稱 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ū)滿時(shí),會(huì)觸發(fā)一次 Minor GC(新生代垃圾回收,也叫 Young GC),將仍然存活的對(duì)象從 Eden 區(qū)和 S0 區(qū)移動(dòng)到 S1 區(qū),下次 Minor GC 處理情況類似,把存活的對(duì)象從 Eden 區(qū)和 S1 區(qū)移動(dòng)到 S0 區(qū)。當(dāng) Survivor 區(qū)也滿了,還存活的對(duì)象會(huì)被移動(dòng)到老年代。如果老年代也滿了,將會(huì)觸發(fā) Major GC(老年代垃圾回收,也叫 Old GC)。當(dāng)老年代滿了,也可能觸發(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)與堆一樣,是所有線程共享的內(nèi)存區(qū)域,用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。

方法區(qū)只是 JVM 規(guī)范中定義的一個(gè)概念,針對(duì) Hotspot 虛擬機(jī),JDK8 之前使用永久代(Permanent Generation,簡(jiǎn)稱 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ǔ)了類和接口中的常量,包括字符串字面量、被聲明為final的常量值等。它還存儲(chǔ)了類和接口中的符號(hào)引用,如類和接口、字段和方法的引用等。

在JVM中,運(yùn)行時(shí)常量池是線程安全的。每個(gè)線程都有一個(gè)自己的線程棧,其中包含了局部變量表,而這些局部變量表中所引用的對(duì)象都位于堆中。當(dāng)一個(gè)線程需要引用運(yùn)行時(shí)常量池中的常量時(shí),JVM會(huì)先將常量值從運(yùn)行時(shí)常量池中復(fù)制到線程棧的局部變量表中,然后再進(jìn)行引用。

需要注意的是,在JDK8中,運(yùn)行時(shí)常量池已經(jīng)被移動(dòng)到元空間(Metaspace)中。元空間是在本地內(nèi)存中分配的,與JVM的堆內(nèi)存是分離的,因此不會(huì)受到Java堆大小的限制。

責(zé)任編輯:武曉燕 來(lái)源: 一燈架構(gòu)
相關(guān)推薦

2021-12-06 11:03:57

JVM性能調(diào)優(yōu)

2021-12-27 08:22:18

Kafka消費(fèi)模型

2023-02-03 07:24:49

雙親委派模型

2021-09-10 18:47:22

Redis淘汰策略

2021-12-09 12:22:28

MyBatis流程面試

2020-07-28 08:59:22

JavahreadLocal面試

2021-07-14 07:21:57

JVM運(yùn)行數(shù)據(jù)

2023-02-07 06:47:58

JVM 模塊Java 虛擬機(jī)

2010-09-25 12:38:40

JVM內(nèi)存模型

2023-02-17 08:02:45

@Autowired@Resource

2019-03-15 19:41:39

MySQL面試數(shù)據(jù)庫(kù)

2023-02-01 07:15:16

2020-11-05 13:12:47

紅黑樹(shù)

2024-01-05 14:20:55

MySQL索引優(yōu)化器

2020-09-29 15:24:07

面試數(shù)據(jù)結(jié)構(gòu)Hashmap

2020-02-18 14:25:51

Java線程池拒絕策略

2023-11-05 12:05:35

JVM內(nèi)存

2018-07-04 14:43:55

對(duì)象模型內(nèi)存結(jié)構(gòu)內(nèi)存模型

2023-02-02 07:06:10

2021-12-13 11:12:41

Spring事務(wù)失效
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)