JVM基礎(chǔ)知識(shí)-內(nèi)存結(jié)構(gòu)

1.前言

本章開(kāi)始學(xué)習(xí)JVM的基礎(chǔ)知識(shí),對(duì)于大部分開(kāi)發(fā)人員來(lái)說(shuō),可能并沒(méi)有花時(shí)間研究JVM的相關(guān)知識(shí),但是他們也能完成一般程序的編寫(xiě).如果我們了解JVM的相關(guān)知識(shí),那么在代碼編寫(xiě),BUG調(diào)試,性能調(diào)優(yōu)方面才能得心應(yīng)手,不再是盲人摸象

2.目錄

目錄

3.JVM基礎(chǔ)

3.1.內(nèi)存結(jié)構(gòu)

  • JVM分為5個(gè)區(qū):虛擬機(jī)棧(VM Stack),本地方法棧(Native Method Stack),方法區(qū)(Method Area),堆(Heap),程序計(jì)數(shù)器(Program Counter Register)
  • 虛擬機(jī)棧,本地方法棧,線程計(jì)數(shù)器為線程私有,方法區(qū)和堆為線程共享區(qū),并發(fā)訪問(wèn)時(shí)會(huì)阻塞
  • JVM不同區(qū)域占用的內(nèi)存大小不同,一般情況下堆占用最大,存放的是對(duì)象,程序計(jì)數(shù)器最小
JVM內(nèi)存結(jié)構(gòu)

3.1.1.堆
堆里面存放是對(duì)象,幾乎所有的對(duì)象實(shí)例都在此分配,隨著優(yōu)化技術(shù)的更新,某些數(shù)據(jù)也會(huì)被放在棧上.因?yàn)槎颜加脙?nèi)存空間最大,堆也是Java垃圾回收的主要區(qū)域,因此也常稱(chēng)作"GC堆"(Garbage Collected Heap).有了GC就是相適應(yīng)的策略,現(xiàn)代收集器基本都采用分代收集算法,堆被細(xì)化如下:

  • 堆的GC操作采用分帶收集算法
  • 堆區(qū)分新生代和老年代
  • 新生代又分為:Eden空間,From Survivor(S0)空間,To Survivor(S1)空間

Java虛擬機(jī)規(guī)范規(guī)定,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可.要增加堆空間時(shí),只需完成邏輯上的擴(kuò),即可,但當(dāng)堆中沒(méi)有內(nèi)存完成實(shí)例分配,并且堆也無(wú)法再擴(kuò)展時(shí),將會(huì)拋出OutOfMemoryError異常

3.1.2.方法區(qū)
方法區(qū)與堆都有線程共享,內(nèi)存不連續(xù)可擴(kuò)展,可垃圾回收,當(dāng)無(wú)法再擴(kuò)展時(shí)會(huì)拋出OutOfMemoryError異常等特性,Java虛擬機(jī)規(guī)范也把方法區(qū)描述為堆的一個(gè)邏輯部分,但目前實(shí)際上是與Java堆分開(kāi)的(Non-Heap)

方法區(qū)存放的是已被虛擬機(jī)加載的類(lèi)信息,常量,靜態(tài)變量,即時(shí)編譯器編譯后的數(shù)據(jù).方法區(qū)的內(nèi)存回收主要目標(biāo)是常量池和類(lèi)型的卸載


方法區(qū)

3.1.3.程序計(jì)數(shù)器
程序計(jì)數(shù)器占用內(nèi)存小,線程私有,唯一沒(méi)有OutOfMemoryError的區(qū)域.其作用是當(dāng)前程序所執(zhí)行字節(jié)碼的行號(hào)指示器,字節(jié)碼解釋器工作時(shí)通過(guò)計(jì)數(shù)器的值來(lái)選取下一條字節(jié)碼指令,操作程序執(zhí)行順序,跳轉(zhuǎn),異常處理,線程恢復(fù)等基礎(chǔ)功能

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

java虛擬機(jī)的多線程是通過(guò)線程輪流切換并分配處理器執(zhí)行時(shí)間來(lái)實(shí)現(xiàn)的,同一時(shí)刻,一個(gè)處理器(對(duì)于多核處理器來(lái)說(shuō)是一個(gè)內(nèi)核)只會(huì)執(zhí)行一條線程中的指令.
程序計(jì)數(shù)器是私有的,這樣才能恢復(fù)到正確的執(zhí)行位置,各條線程之間的計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ).執(zhí)行Java方法是記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址,執(zhí)行Native方法計(jì)算器的值為空(Undefined)

3.1.4.虛擬機(jī)棧
虛擬機(jī)棧線程私有,生命周期與線程相同,每個(gè)方法執(zhí)行都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame),棧幀用于虛擬機(jī)進(jìn)行方法調(diào)用的數(shù)據(jù)結(jié)構(gòu),棧幀包含局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈接和方法返回地址等信息.每一個(gè)方法從調(diào)用至執(zhí)行的過(guò)程,對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)從入棧到出棧的過(guò)程

虛擬機(jī)棧

  • 局部變量表(Local Variable Table):變量值的存儲(chǔ)空間,存放方法參數(shù)和方法內(nèi)定義的局部變量.包括8種基本數(shù)據(jù)類(lèi)型,引用類(lèi)型(reference類(lèi)型)和returnAddress類(lèi)型(指向一條字節(jié)碼指令的地址)
  • 如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,將拋出StackOverflowError,
    如果虛擬機(jī)動(dòng)態(tài)擴(kuò)展無(wú)法申請(qǐng)到足夠的內(nèi)存時(shí)會(huì)拋出OutOfMemoryError
  • 操作數(shù)棧(Operand Stack):也稱(chēng)操作棧,隨著方法的執(zhí)行,會(huì)從局部變量表或?qū)ο髮?shí)例的字段中復(fù)制常量或變量寫(xiě)入到操作數(shù)棧(入棧),再隨著計(jì)算的進(jìn)行,將棧中的元素出棧到局部變量表或者返回給方法的調(diào)用者()出棧
  • 動(dòng)態(tài)鏈接(Dynamic Linking):每個(gè)棧幀都包含一個(gè)指向運(yùn)行時(shí)常量池中該棧所屬方法的符號(hào)引用,class文件的常量池中有大量的符號(hào)引用,字節(jié)碼中方法的調(diào)用指令就以常量池中方法的符號(hào)引用為參數(shù),這些符號(hào)引用一部分會(huì)在類(lèi)加載階段或者第一次使用的時(shí)候轉(zhuǎn)化為直接引用(靜態(tài)方法,私有方法),這種轉(zhuǎn)化稱(chēng)為靜態(tài)解析;另外一部分將在每一次運(yùn)行期間轉(zhuǎn)化為直接引用,這部分稱(chēng)為動(dòng)態(tài)鏈接
  • 方法返回:無(wú)論方法是否正常完成,都需要返回到方法被調(diào)用的位置,程序才能繼續(xù)進(jìn)行

3.1.5.本地方法棧
與虛擬機(jī)棧作用相似,也會(huì)拋出StackOverflowError和OutOfMemoryError,區(qū)別在于虛擬機(jī)棧為虛擬機(jī)執(zhí)行java方法服務(wù),而本地方法棧視為Native方法服務(wù)

3.2.JDK8 JVM內(nèi)存結(jié)構(gòu)

堆和方法區(qū)的物理存儲(chǔ)(方法區(qū)位于永久代中)
  • 堆和方法區(qū)是在同一塊連續(xù)的內(nèi)存,但是在邏輯上它們是分開(kāi)的
  • HotSport將GC分代擴(kuò)展至方法區(qū),即用永久代(Permanent Generation)來(lái)實(shí)現(xiàn)方法區(qū),其它虛擬機(jī)沒(méi)有永久代的概念.方法區(qū)是規(guī)范,永久代是hotSport針對(duì)該規(guī)范的實(shí)現(xiàn)
  • 永久代和老年代是捆綁在一起的,無(wú)論誰(shuí)滿了,都將觸發(fā)永久代和老年代的垃圾收集
JDK8內(nèi)存結(jié)構(gòu)
  • JDK8中,HotSport取消了永久代,取而代之的是元空間(MetaSpace),方法區(qū)存在于元空間,元空間不再與堆連續(xù),而是存在于本地內(nèi)存
  • 本地內(nèi)存(Native Memory),也稱(chēng)C-Heap,供JVM自身進(jìn)程使用,當(dāng)Java Heap空間不足不足時(shí)會(huì)觸發(fā)GC,C-Heap空間不足時(shí)卻不會(huì)觸發(fā)GC.默認(rèn)情況下可以無(wú)限使用本地內(nèi)存,可設(shè)置參數(shù)限制其使用
  • 使用元空間可由系統(tǒng)的實(shí)際可用空間來(lái)控制,不再由參數(shù)配置上限,減少了OOM的產(chǎn)生,且java8的HotSport就是以前的HotSport和JRockit的合并版,JRockit沒(méi)有所謂的永久代

4.總結(jié)

本章講解JVM的內(nèi)存結(jié)構(gòu),對(duì)于數(shù)據(jù)的存儲(chǔ)和運(yùn)行有了簡(jiǎn)單的了解,知道了當(dāng)虛擬機(jī)無(wú)法在擴(kuò)展更多的內(nèi)存,且不足以存儲(chǔ)時(shí)會(huì)拋出OutOfMemoryError異常,當(dāng)棧深度超過(guò)虛擬機(jī)分配給線程的棧的大小時(shí)會(huì)拋出StackOverflowError異常


參考博客:
https://www.zhihu.com/question/29265430
https://juejin.im/post/5da9446751882508866e960b
https://juejin.im/post/5dae7e5de51d45249850cf9f#heading-0

最后編輯于
?著作權(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)容