Java虛擬機(jī)內(nèi)存結(jié)構(gòu)

一、jvm概述

jvm又叫java虛擬機(jī)(The Java Virtual Machine),主要有三種功能:運(yùn)行編譯的class文件廊镜,內(nèi)存分配和垃圾回收即碗,提供多線程的同步。參考:Java Virtual Machine Technology Overview

二搞疗、jvm內(nèi)存模型

jvm內(nèi)存模型是指java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)里面包含了哪些東西嗓蘑。

JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)

三、JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)

jvm-framework

Java 虛擬機(jī)定義了若干種程序運(yùn)行期間會(huì)使用到的運(yùn)行時(shí)數(shù)據(jù)區(qū)匿乃,其中有一些會(huì)隨著虛擬機(jī)啟動(dòng)而創(chuàng)建桩皿,隨著虛擬機(jī)退出而銷毀。另外一些則是與線程一一對(duì)應(yīng)的幢炸,這些與線程一一對(duì)應(yīng)的數(shù)據(jù)區(qū)域會(huì)隨著線程開始和結(jié)束而創(chuàng)建和銷毀泄隔。

線程私有:程序計(jì)數(shù)器、棧宛徊、本地棧

線程共享:堆佛嬉、堆外內(nèi)存(永久代或元空間、代碼緩存)

方法區(qū)方法區(qū)(method area)只是JVM規(guī)范中定義的一個(gè)概念闸天,用于存儲(chǔ)類信息暖呕、常量池、靜態(tài)變量苞氮、JIT編譯后的代碼等數(shù)據(jù)湾揽,并沒有規(guī)定如何去實(shí)現(xiàn)它,不同的廠商有不同的實(shí)現(xiàn)。而永久代(PermGen)是?Hotspot?虛擬機(jī)特有的概念库物, Java8 的時(shí)候又被元空間取代了霸旗,永久代和元空間都可以理解為方法區(qū)的落地實(shí)現(xiàn)。

堆內(nèi)存:Java 堆是 Java 虛擬機(jī)管理的內(nèi)存中最大的一塊戚揭,被所有線程共享定硝。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例以及數(shù)據(jù)都在這里分配內(nèi)存毫目。

棧內(nèi)存:主管 Java 程序的運(yùn)行蔬啡,它保存方法的局部變量、部分結(jié)果镀虐,并參與方法的調(diào)用和返回箱蟆。

本地方法棧:一個(gè) Native Method 就是一個(gè) Java 調(diào)用非 Java 代碼的接口。我們知道的 Unsafe 類就有很多本地方法刮便。Java 虛擬機(jī)棧用于管理 Java 方法的調(diào)用空猜,而本地方法棧用于管理本地方法的調(diào)用。

程序計(jì)數(shù)器:程序計(jì)數(shù)器是一塊較小的內(nèi)存空間恨旱,可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器辈毯。由字節(jié)碼執(zhí)行引擎直接修改。

具體的一個(gè)代碼例子
上面例子在jvm中的關(guān)系

四搜贤、程序計(jì)數(shù)器

PC 寄存器用來存儲(chǔ)指向下一條指令的地址谆沃,即將要執(zhí)行的指令代碼。由執(zhí)行引擎讀取下一條指令仪芒。

程序計(jì)數(shù)器是一塊較小的內(nèi)存空間唁影,可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器

使用PC寄存器存儲(chǔ)字節(jié)碼指令地址有什么用呢掂名?為什么使用PC寄存器記錄當(dāng)前線程的執(zhí)行地址呢据沈?

???♂?:因?yàn)镃PU需要不停的切換各個(gè)線程,這時(shí)候切換回來以后饺蔑,就得知道接著從哪開始繼續(xù)執(zhí)行锌介。JVM的字節(jié)碼解釋器就需要通過改變PC寄存器的值來明確下一條應(yīng)該執(zhí)行什么樣的字節(jié)碼指令。

PC寄存器為什么會(huì)被設(shè)定為線程私有的猾警?

???♂?:多線程在一個(gè)特定的時(shí)間段內(nèi)只會(huì)執(zhí)行其中某一個(gè)線程方法孔祸,CPU會(huì)不停的做任務(wù)切換,這樣必然會(huì)導(dǎo)致經(jīng)常中斷或恢復(fù)肿嘲。為了能夠準(zhǔn)確的記錄各個(gè)線程正在執(zhí)行的當(dāng)前字節(jié)碼指令地址融击,所以為每個(gè)線程都分配了一個(gè)PC寄存器,每個(gè)線程都獨(dú)立計(jì)算雳窟,不會(huì)互相影響。

通過javap反編譯class

五、虛擬機(jī)棧

Java 虛擬機(jī)棧(Java Virtual Machine Stacks)封救,早期也叫 Java 棧拇涤。每個(gè)線程在創(chuàng)建的時(shí)候都會(huì)創(chuàng)建一個(gè)虛擬機(jī)棧,其內(nèi)部保存一個(gè)個(gè)的棧幀(Stack Frame)誉结,對(duì)應(yīng)著一次次 Java 方法調(diào)用鹅士,是線程私有的,生命周期和線程一致惩坑。

作用:主管 Java 程序的運(yùn)行掉盅,它保存方法的局部變量、部分結(jié)果以舒,并參與方法的調(diào)用和返回趾痘。

Java 虛擬機(jī)規(guī)范允許?Java虛擬機(jī)棧的大小是動(dòng)態(tài)的或者是固定不變的

如果采用固定大小的 Java 虛擬機(jī)棧,那每個(gè)線程的 Java 虛擬機(jī)棧容量可以在線程創(chuàng)建的時(shí)候獨(dú)立選定蔓钟。如果線程請(qǐng)求分配的棧容量超過 Java 虛擬機(jī)棧允許的最大容量永票,Java 虛擬機(jī)將會(huì)拋出一個(gè)?StackOverflowError?異常

如果 Java 虛擬機(jī)棧可以動(dòng)態(tài)擴(kuò)展滥沫,并且在嘗試擴(kuò)展的時(shí)候無法申請(qǐng)到足夠的內(nèi)存侣集,或者在創(chuàng)建新的線程時(shí)沒有足夠的內(nèi)存去創(chuàng)建對(duì)應(yīng)的虛擬機(jī)棧,那 Java 虛擬機(jī)將會(huì)拋出一個(gè)OutOfMemoryError異常

可以通過參數(shù)-Xss來設(shè)置線程的最大椑夹澹空間世分,棧的大小直接決定了函數(shù)調(diào)用的最大可達(dá)深度∽罕纾可參考JVMMemorySetting.

每個(gè)**棧幀(Stack Frame)**中存儲(chǔ)著:

局部變量表(Local Variables):是一組變量值存儲(chǔ)空間罚攀,主要用于存儲(chǔ)方法參數(shù)和定義在方法體內(nèi)的局部變量,包括編譯器可知的各種 Java 虛擬機(jī)基本數(shù)據(jù)類型(boolean雌澄、byte斋泄、char、short镐牺、int炫掐、float、long睬涧、double)募胃、對(duì)象引用(reference類型,它并不等同于對(duì)象本身畦浓,可能是一個(gè)指向?qū)ο笃鹗嫉刂返囊弥羔槺允部赡苁侵赶蛞粋€(gè)代表對(duì)象的句柄或其他與此相關(guān)的位置)和returnAddress?類型(指向了一條字節(jié)碼指令的地址,已被異常表取代)讶请,局部變量表中的變量也是重要的垃圾回收根節(jié)點(diǎn)祷嘶,只要被局部變量表中直接或間接引用的對(duì)象都不會(huì)被回收.

操作數(shù)棧(Operand Stack)(或稱為表達(dá)式棧):操作數(shù)棧,在方法執(zhí)行過程中,根據(jù)字節(jié)碼指令论巍,往操作數(shù)棧中寫入數(shù)據(jù)或提取數(shù)據(jù)烛谊,即入棧(push)、出棧(pop)嘉汰,操作數(shù)棧丹禀,主要用于保存計(jì)算過程的中間結(jié)果,同時(shí)作為計(jì)算過程中變量臨時(shí)的存儲(chǔ)空間鞋怀。

動(dòng)態(tài)鏈接(Dynamic Linking):指向運(yùn)行時(shí)常量池的方法引用双泪,每一個(gè)棧幀內(nèi)部都包含一個(gè)指向運(yùn)行時(shí)常量池中該棧幀所屬方法的引用。包含這個(gè)引用的目的就是為了支持當(dāng)前方法的代碼能夠?qū)崿F(xiàn)動(dòng)態(tài)鏈接(Dynamic Linking)密似。

在 Java 源文件被編譯到字節(jié)碼文件中時(shí)焙矛,所有的變量和方法引用都作為符號(hào)引用(Symbolic Reference)保存在 Class 文件的常量池中。比如:描述一個(gè)方法調(diào)用了另外的其他方法時(shí)辛友,就是通過常量池中指向方法的符號(hào)引用來表示的薄扁,那么動(dòng)態(tài)鏈接的作用就是為了將這些符號(hào)引用轉(zhuǎn)換為調(diào)用方法的直接引用。

動(dòng)態(tài)鏈接(Dynamic Linking)

方法返回地址(Return Address):方法正常退出或異常退出的地址

一些附加信息.

棧幀

六废累、本地方法棧(Native Method Stack)

Java 虛擬機(jī)棧用于管理 Java 方法的調(diào)用邓梅,而本地方法棧用于管理本地方法的調(diào)用

本地方法棧也是線程私有的

允許線程固定或者可動(dòng)態(tài)擴(kuò)展的內(nèi)存大小,本地方法可以通過本地方法接口來訪問虛擬機(jī)內(nèi)部的運(yùn)行時(shí)數(shù)據(jù)區(qū)邑滨,它甚至可以直接使用本地處理器中的寄存器日缨,直接從本地內(nèi)存的堆中分配任意數(shù)量的內(nèi)存。

棧是運(yùn)行時(shí)的單位掖看,而堆是存儲(chǔ)的單位匣距。棧解決程序的運(yùn)行問題,即程序如何執(zhí)行哎壳,或者說如何處理數(shù)據(jù)毅待。堆解決的是數(shù)據(jù)存儲(chǔ)的問題,即數(shù)據(jù)怎么放归榕、放在哪尸红。

七、堆內(nèi)存

對(duì)于大多數(shù)應(yīng)用刹泄,Java 堆是 Java 虛擬機(jī)管理的內(nèi)存中最大的一塊外里,被所有線程共享。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例特石,幾乎所有的對(duì)象實(shí)例以及數(shù)據(jù)都在這里分配內(nèi)存盅蝗。

為了進(jìn)行高效的垃圾回收,虛擬機(jī)把堆內(nèi)存邏輯上劃分成三塊區(qū)域(分代的唯一理由就是優(yōu)化 GC 性能):

新生帶(年輕代):新對(duì)象和沒達(dá)到一定年齡的對(duì)象都在新生代

老年代(養(yǎng)老區(qū)):被長時(shí)間使用的對(duì)象姆蘸,老年代的內(nèi)存空間應(yīng)該要比年輕代更大

元空間(JDK1.8之前叫永久代):像一些方法中的操作臨時(shí)對(duì)象等墩莫,JDK1.8之前是占用JVM內(nèi)存芙委,JDK1.8之后直接使用物理內(nèi)存。

堆內(nèi)存

7.1贼穆、 對(duì)象的分配過程

為對(duì)象分配內(nèi)存是一件非常嚴(yán)謹(jǐn)和復(fù)雜的任務(wù)题山,JVM 的設(shè)計(jì)者們不僅需要考慮內(nèi)存如何分配兰粉、在哪里分配等問題故痊,并且由于內(nèi)存分配算法和內(nèi)存回收算法密切相關(guān),所以還需要考慮 GC 執(zhí)行完內(nèi)存回收后是否會(huì)在內(nèi)存空間中產(chǎn)生內(nèi)存碎片玖姑。

1愕秫、new 的對(duì)象先放在伊甸園區(qū),此區(qū)有大小限制

2焰络、當(dāng)伊甸園的空間填滿時(shí)戴甩,程序又需要?jiǎng)?chuàng)建對(duì)象,JVM 的垃圾回收器將對(duì)伊甸園區(qū)進(jìn)行垃圾回收(Minor GC)闪彼,將伊甸園區(qū)中的不再被其他對(duì)象所引用的對(duì)象進(jìn)行銷毀甜孤。再加載新的對(duì)象放到伊甸園區(qū)

3、然后將伊甸園中的剩余對(duì)象移動(dòng)到幸存者 0 區(qū)

4畏腕、如果再次觸發(fā)垃圾回收缴川,此時(shí)上次幸存下來的放到幸存者 0 區(qū),如果沒有回收描馅,就會(huì)放到幸存者 1 區(qū)

5把夸、如果再次經(jīng)歷垃圾回收,此時(shí)會(huì)重新放回幸存者 0 區(qū)铭污,接著再去幸存者 1 區(qū)

6恋日、什么時(shí)候才會(huì)去養(yǎng)老區(qū)呢? 默認(rèn)是 15 次回收標(biāo)記

7嘹狞、在養(yǎng)老區(qū)岂膳,相對(duì)悠閑。當(dāng)養(yǎng)老區(qū)內(nèi)存不足時(shí)磅网,再次觸發(fā) Major GC谈截,進(jìn)行養(yǎng)老區(qū)的內(nèi)存清理

8、若養(yǎng)老區(qū)執(zhí)行了 Major GC ?之后發(fā)現(xiàn)依然無法進(jìn)行對(duì)象的保存知市,就會(huì)產(chǎn)生 OOM 異常

7.2傻盟、 GC 垃圾回收簡介

可以使用jvisualvm來查看當(dāng)前線程分配內(nèi)存的情況。

通過GC Roots標(biāo)記非垃圾對(duì)象

Minor GC嫂丙、Major GC娘赴、Full GC

JVM 在進(jìn)行 GC 時(shí),并非每次都對(duì)堆內(nèi)存(新生代跟啤、老年代诽表;方法區(qū))區(qū)域一起回收的唉锌,大部分時(shí)候回收的都是指新生代。

針對(duì) HotSpot VM 的實(shí)現(xiàn)竿奏,它里面的 GC 按照回收區(qū)域又分為兩大類:部分收集(Partial GC)袄简,整堆收集(Full ?GC)

部分收集:不是完整收集整個(gè) Java 堆的垃圾收集。其中又分為:

????目前只有 G1 GC 會(huì)有這種行為

????目前泛啸,只有 CMS GC 會(huì)有單獨(dú)收集老年代的行為

????很多時(shí)候 Major GC 會(huì)和 Full GC ?混合使用绿语,需要具體分辨是老年代回收還是整堆回收

????新生代收集(Minor GC/Young GC):只是新生代的垃圾收集

????老年代收集(Major GC/Old GC):只是老年代的垃圾收集

????混合收集(Mixed GC):收集整個(gè)新生代以及部分老年代的垃圾收集

整堆收集(Full GC):收集整個(gè) Java 堆和方法區(qū)的垃圾, stop world.

垃圾回收機(jī)制比較復(fù)雜、堆內(nèi)存分配推薦看視頻JAVA虛擬機(jī)和垃圾回收機(jī)制大講解?和?垃圾回收機(jī)制候址。

堆內(nèi)存的survivor區(qū)-jvm調(diào)優(yōu)減少full gc

full gc?stop world的目的是防止用戶線程結(jié)束吕粹,gc的內(nèi)存無效導(dǎo)致繁瑣的重復(fù)gc.

gc類型

gc的一個(gè)例子:當(dāng)對(duì)象大小超過servivor的50%,直接進(jìn)入老年代。

gc
gc的例子

八岗仑、方法區(qū)

關(guān)系圖

運(yùn)行時(shí)常量池

運(yùn)行時(shí)常量池(Runtime Constant Pool)是方法區(qū)的一部分匹耕,理解運(yùn)行時(shí)常量池的話,我們先來說說字節(jié)碼文件(Class 文件)中的常量池(常量池表)

常量池:一個(gè)有效的字節(jié)碼文件中除了包含類的版本信息荠雕、字段稳其、方法以及接口等描述信息外,還包含一項(xiàng)信息那就是常量池表(Constant Pool Table)炸卑,包含各種字面量和對(duì)類型既鞠、域和方法的符號(hào)引用。

為什么需要常量池矾兜?

答:一個(gè) java 源文件中的類损趋、接口,編譯后產(chǎn)生一個(gè)字節(jié)碼文件椅寺。而 Java 中的字節(jié)碼需要數(shù)據(jù)支持浑槽,通常這種數(shù)據(jù)會(huì)很大以至于不能直接存到字節(jié)碼里,換另一種方式返帕,可以存到常量池桐玻,這個(gè)字節(jié)碼包含了指向常量池的引用。在動(dòng)態(tài)鏈接的時(shí)候用到的就是運(yùn)行時(shí)常量池荆萤。

運(yùn)行時(shí)常量池

在加載類和結(jié)構(gòu)到虛擬機(jī)后镊靴,就會(huì)創(chuàng)建對(duì)應(yīng)的運(yùn)行時(shí)常量池

常量池表(Constant Pool Table)是 Class 文件的一部分,用于存儲(chǔ)編譯期生成的各種字面量和符號(hào)引用链韭,這部分內(nèi)容將在類加載后存放到方法區(qū)的運(yùn)行時(shí)常量池中

JVM 為每個(gè)已加載的類型(類或接口)都維護(hù)一個(gè)常量池偏竟。池中的數(shù)據(jù)項(xiàng)像數(shù)組項(xiàng)一樣,是通過索引訪問的

運(yùn)行時(shí)常量池中包含各種不同的常量敞峭,包括編譯器就已經(jīng)明確的數(shù)值字面量踊谋,也包括到運(yùn)行期解析后才能夠獲得的方法或字段引用。此時(shí)不再是常量池中的符號(hào)地址了旋讹,這里換為真實(shí)地址

運(yùn)行時(shí)常量池殖蚕,相對(duì)于 Class 文件常量池的另一個(gè)重要特征是:動(dòng)態(tài)性轿衔,Java 語言并不要求常量一定只有編譯期間才能產(chǎn)生,運(yùn)行期間也可以將新的常量放入池中睦疫,String 類的?intern()?方法就是這樣的

當(dāng)創(chuàng)建類或接口的運(yùn)行時(shí)常量池時(shí)害驹,如果構(gòu)造運(yùn)行時(shí)常量池所需的內(nèi)存空間超過了方法區(qū)所能提供的最大值,則 JVM 會(huì)拋出 OutOfMemoryError 異常

參考文檔:

虛擬機(jī)講解

https://docs.oracle.com/en/java/javase/14/

https://docs.oracle.com/javase/specs/jvms/se14/html/jvms-2.html

The Structure of the Java Virtual Machine

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛤育,一起剝皮案震驚了整個(gè)濱河市宛官,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缨伊,老刑警劉巖摘刑,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件进宝,死亡現(xiàn)場(chǎng)離奇詭異刻坊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)党晋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門谭胚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人未玻,你說我怎么就攤上這事灾而。” “怎么了扳剿?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵旁趟,是天一觀的道長。 經(jīng)常有香客問我庇绽,道長锡搜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任瞧掺,我火速辦了婚禮耕餐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辟狈。我一直安慰自己肠缔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布哼转。 她就那樣靜靜地躺著明未,像睡著了一般。 火紅的嫁衣襯著肌膚如雪壹蔓。 梳的紋絲不亂的頭發(fā)上趟妥,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音庶溶,去河邊找鬼煮纵。 笑死懂鸵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的行疏。 我是一名探鬼主播匆光,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼酿联!你這毒婦竟也來了终息?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤贞让,失蹤者是張志新(化名)和其女友劉穎周崭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喳张,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡续镇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了销部。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摸航。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖舅桩,靈堂內(nèi)的尸體忽然破棺而出酱虎,到底是詐尸還是另有隱情,我是刑警寧澤擂涛,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布读串,位于F島的核電站,受9級(jí)特大地震影響撒妈,放射性物質(zhì)發(fā)生泄漏恢暖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一踩身、第九天 我趴在偏房一處隱蔽的房頂上張望胀茵。 院中可真熱鬧,春花似錦挟阻、人聲如沸琼娘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脱拼。三九已至,卻和暖如春坷备,著一層夾襖步出監(jiān)牢的瞬間熄浓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赌蔑,地道東北人俯在。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像娃惯,于是被迫代替她去往敵國和親跷乐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359