jvm內(nèi)存結(jié)構(gòu),java內(nèi)存模型,java對象布局還分不清?快來圍觀!

hello 我是寶哥 , 接上一篇文章,我們聊到了

JVM加載類的流程

有面試官會讓你解釋一下Java的內(nèi)存模型,有些人解釋對了,結(jié)果面試官說不對,應(yīng)該是堆啊、棧啊、方法區(qū)什么的(遇到這種面試官,就是你裝逼的時刻了..)

看完本篇文章你將了解:

  • 1.JVM內(nèi)存結(jié)構(gòu)
  • 2.JVM棧幀剖析
  • 3.方法區(qū)在JDK1.7和1.8中的區(qū)別
  • 4.堆分代結(jié)構(gòu)

建議收藏!

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

首先JVM內(nèi)存結(jié)構(gòu)JAVA內(nèi)存模型是兩個概念.

JVM內(nèi)存結(jié)構(gòu):

Class文件通過類加載機制 加載到內(nèi)存空間,JVM內(nèi)存結(jié)構(gòu)就是上圖中內(nèi)存空間,Java內(nèi)存模型,則是另外的一個概念.

根據(jù)《Java 虛擬機規(guī)范(Java SE 7 版)》規(guī)定,Java 虛擬機所管理的內(nèi)存如下圖所示。


從上圖可看出,運行時數(shù)據(jù)區(qū)分為五大區(qū)域。這些區(qū)域各有各的用途,其中方法區(qū)和堆是所有線程共享的,棧,本地方法棧和程序計數(shù)器則為線程私有的。

1.程序計數(shù)器

程序計數(shù)器是一塊很小的內(nèi)存空間,它是線程私有的,可以認(rèn)作為當(dāng)前線程的行號指示器。

為什么要程序計數(shù)器呢

因為CPU會在多個線程中切換上下文,需要使用程序計數(shù)器紀(jì)錄當(dāng)前線程運行到哪一行了,等待線程重新獲取到運行時間時,繼續(xù)從計數(shù)的位置往下執(zhí)行.至于它是線程私有的,是因為每個線程都需要獨立計數(shù),各個線程之間不會產(chǎn)生影響.

面試題:程序計數(shù)器會發(fā)生OutOfMemoryError嗎?

答:這塊內(nèi)存區(qū)域是虛擬機規(guī)范中唯一沒有OutOfMemoryError的區(qū)域。如果線程執(zhí)行的是個java方法,那么計數(shù)器記錄虛擬機字節(jié)碼指令的地址。如果為native方法,那么計數(shù)器為空。

2.Java棧(JVM線程棧)

首先我們要記住,棧描述的是方法執(zhí)行的內(nèi)存模型,它是線程私有的.

每個方法被執(zhí)行的時候都會創(chuàng)建一個棧幀用于存儲局部變量表,操作棧,動態(tài)鏈接,方法出口等信息。每一個方法被調(diào)用的過程就對應(yīng)一個棧幀在虛擬機棧中從入棧到出棧的過程,如下圖:

面試題:Java虛擬機棧可能出現(xiàn)哪兩種類型的異常?

  • 1 虛擬機棧是一個棧,當(dāng)我們的棧幀超過最大深度時,會拋出StackOverflowError
  • 2 棧無法申請到足夠的空間時,拋出OutOfMemoryError異常

棧幀(Stack Frame)

每一個方法從調(diào)用至執(zhí)行完成的過程,都對應(yīng)著一個棧幀在虛擬機棧里從入棧到出棧的過程,那么一個棧幀包含什么?

  • 局部變量表(Local Variable Table)
    也叫本地變量表,它所需要的內(nèi)存空間在編譯期完成分配,當(dāng)進(jìn)入一個方法時,這個方法在棧中需要分配多大的局部變量空間是完全確定的,在方法運行期間不會改變,它的最小單位為Slot,一個Slot可以存放一個32位以內(nèi)的數(shù)據(jù)類型.虛擬機通過索引定位的方法查找相應(yīng)的局部變量,索引的范圍是從0~局部變量表最大容量。如果Slot是32位的,則遇到一個64位數(shù)據(jù)類型的變量(如long或double型),則會使用兩個連續(xù)的Slot來存儲

  • 操作數(shù)棧(Operand Stack)
    也常稱為操作棧,它是一個后入先出棧(LIFO)。當(dāng)一個方法剛剛開始執(zhí)行時,其操作數(shù)棧是空的,隨著方法執(zhí)行和字節(jié)碼指令的執(zhí)行,會從局部變量表或?qū)ο髮嵗淖侄沃袕?fù)制常量或變量寫入到操作數(shù)棧,再隨著計算的進(jìn)行將棧中元素出棧到局部變量表或者返回給方法調(diào)用者,也就是出棧/入棧操作。一個完整的方法執(zhí)行期間往往包含多個這樣出棧/入棧的過程。

  • 動態(tài)連接(Dynamic Linking)
    在一個class文件中,一個方法要調(diào)用其他方法,需要將這些方法的符號引用轉(zhuǎn)化為其在內(nèi)存地址中的直接引用,而符號引用存在于方法區(qū)中的運行時常量池。
    Java虛擬機棧中,每個棧幀都包含一個指向運行時常量池中該棧所屬方法的符號引用,持有這個引用的目的是為了支持方法調(diào)用過程中的動態(tài)連接(Dynamic Linking)。
    這些符號引用一部分會在類加載階段或者第一次使用時就直接轉(zhuǎn)化為直接引用,這類轉(zhuǎn)化稱為靜態(tài)解析。另一部分將在每次運行期間轉(zhuǎn)化為直接引用,這類轉(zhuǎn)化稱為動態(tài)連接

  • 方法返回地址(Return address)
    一般方法執(zhí)行時,有2種方式會退出該方法

    • 1.正常退出
      正常退出指方法正常完成并退出,沒有拋出任何異常,當(dāng)前方法正常完成,則根據(jù)當(dāng)前方法返回的字節(jié)碼指令,這時有可能會有返回值傳遞給方法調(diào)用者(調(diào)用它的方法),或者無返回值(void)。具體是否有返回值以及返回值的數(shù)據(jù)類型將根據(jù)該方法返回的字節(jié)碼指令確定。
    • 2.異常退出
      方法執(zhí)行過程中遇到異常,并且這個異常在方法體內(nèi)部沒有得到處理,導(dǎo)致方法退出,只要在本方法的異常表中沒有搜索到相應(yīng)的異常處理器,就會導(dǎo)致方法退出。

    方法退出過程實際上就等同于把當(dāng)前棧幀出棧,因此退出可以執(zhí)行的操作有:恢復(fù)上層方法的局部變量表和操作數(shù)棧,把返回值(如果有的話)壓如調(diào)用者的操作數(shù)棧中,調(diào)整PC計數(shù)器的值以指向方法調(diào)用指令后的下一條指令。
    一般來說,方法正常退出時,調(diào)用者的PC計數(shù)值可以作為返回地址,棧幀中可能保存此計數(shù)值。而方法異常退出時,返回地址是通過異常處理器表確定的,棧幀中一般不會保存此部分信息

  • 附加信息
    指的是在虛擬機實現(xiàn)中加入了一些規(guī)范里沒有描述的信息到棧幀之中,例如與調(diào)試相關(guān)的信息。

本地方法棧

本地方法棧是與虛擬機棧發(fā)揮的作用十分相似,區(qū)別是虛擬機棧執(zhí)行的是Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則為虛擬機使用到的native方法服務(wù),可能底層調(diào)用的c或者c++,我們打開jdk安裝目錄可以看到也有很多用c編寫的文件,可能就是native方法所調(diào)用的c代碼。

java是跨平臺的語言,既然是跨了平臺,所付出的代價就是犧牲一些對底層的控制,而java要實現(xiàn)對底層的控制,就需要一些其他語言的幫助,這個就是native的作用了.

它是所有線程共享的,它的目的是存放對象實例。同時它也是GC所管理的主要區(qū)域,因此常被稱為GC堆,現(xiàn)在收集器常使用分代算法進(jìn)行垃圾回收,這里是垃圾回收重災(zāi)區(qū).

堆分代結(jié)構(gòu)

堆被劃分成兩個不同的區(qū)域:新生代 ( Young )、老年代 ( Old )。

新生代 ( Young ) 又被劃分為三個區(qū)域:Eden、From Survivor、To Survivor。它們的默認(rèn)比例關(guān)系如下圖:


  • 新生代 (Young Generation)
    新生成的對象優(yōu)先存放在新生代中,新生代對象朝生夕死,存活率很低,在新生代中常規(guī)應(yīng)用進(jìn)行一次垃圾收集一般可以回收70%~95%的空間,回收效率很高

  • 幸存者區(qū) (Survivor)
    幸存者區(qū)有2塊, From Survivor區(qū), To Survivor區(qū),也稱S0,S1區(qū).被面試官問了不要懵,GC進(jìn)行時,Eden區(qū)中所有存活的對象都會被復(fù)制到 To Survivor區(qū),而在FromSurvivor區(qū)中,仍存活的對象會根據(jù)它們的年齡值決定去向,年齡值達(dá)到年齡閥值(默認(rèn)為15,新生代中的對象毎熬過一輪垃圾回收,年齡值就加1,GC分代年齡存儲在對象的 header中)的對象會被移到老年代中,沒有達(dá)到閥值的對象會被復(fù)制到 To Survivor區(qū)。接著清空Eden區(qū)和 From Survivor區(qū),新生代中存活的對象都在 To Survivor區(qū)。接著,F(xiàn)rom Survivor區(qū)和 To Survivor區(qū)會交換它們的角色,GC時當(dāng) To Survivor區(qū)沒有足夠的空間存放上一次新生代收集下來的存活對象時,需要依賴?yán)夏甏M(jìn)行分配擔(dān)保,將這些對象存放在老年代中.

  • 老年代(Old)
    在新生代中經(jīng)歷了多次(具體看虛擬機配置的值)GC后仍然存活下來的對象會進(jìn)入老年代中。老年代中的對象生命周期較長,存活率比較高,在老年代中進(jìn)行GC的頻率相對而言較低,而且回收的速度也比較慢


常見面試題:

  • 何時發(fā)生Minor GC/Young GC ?
    新生成的對象在Eden區(qū)分配(大對象除外,大對象直接進(jìn)入老年代),當(dāng)Eden區(qū)沒有足夠的空間進(jìn)行分配時,虛擬機將發(fā)起一次 Minor GC也叫Young GC
  • 為什么分代?
    將對象根據(jù)存活概率進(jìn)行分類,對存活時間長的對象,放到固定區(qū),從而減少掃描垃圾時間及GC頻率。針對分類進(jìn)行不同的垃圾回收算法,對算法揚長避短

方法區(qū)


從這張圖可以看到JDK1.8和JDK1.7相比最大的區(qū)別是:元空間區(qū)取代了永久代,永久代原本主要存放ClassMeta的信息。而元空間的本質(zhì)和永久代類似,都是對JVM規(guī)范中方法區(qū)的實現(xiàn)。不過元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機中,而是使用本地內(nèi)存。因此,默認(rèn)情況下,元空間的大小僅受本地內(nèi)存限制。

上圖中我們可以看到,JDK1.7和1.8對于運行時數(shù)據(jù)區(qū)和堆中的方法區(qū)都做了調(diào)整,jdk8中引入了一個新的內(nèi)存區(qū)域叫metaspace。并不是所有的jvm中都有永久代,IBM的J9,oracle的JRocket都沒有永久代,永久代是實現(xiàn)層面的東西,永久代里面存的東西基本上就是方法區(qū)規(guī)定的那些東西。


面試題: 方法區(qū)、永久代、元空間的區(qū)別?

  • 方法區(qū)
    是JVM的規(guī)范,所有虛擬機必須遵守的。是JVM 所有線程共享的、用于存儲類的信息、常量池、方法數(shù)據(jù)、方法代碼等。
  • 永久代:
    全稱Permanent Generation space ,是指內(nèi)存的永久保存區(qū)域。是 HotSpot 虛擬機基于JVM規(guī)范對方法區(qū)的一個落地實現(xiàn),并且只有 HotSpot 才有 PermGen space,在JDK8被移除了.
  • 元空間:
    元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機中,而是使用本地內(nèi)存。它也是基于JVM規(guī)范對方法區(qū)的一個落地實現(xiàn)

JDK6、JDK7 時,方法區(qū) 就是 PermGen(永久代)。
JDK8 時,方法區(qū)就是 Metaspace(元空間)

不同版本的JDK中 堆溢出的實例:


面試題:為什么去除了永久代:

1)字符串存在永久代中,容易出現(xiàn)性能問題和內(nèi)存溢出。

2)類及方法的信息等比較難確定其大小,因此對于永久代的大小指定比較困難,太小容易出現(xiàn)永久代溢出,太大則容易導(dǎo)致老年代溢出。

3)永久代會為 GC 帶來不必要的復(fù)雜度,并且回收效率偏低。

結(jié)語

經(jīng)過本篇學(xué)習(xí),我們已經(jīng)知道JVM對于內(nèi)存的管理,以及它的結(jié)構(gòu),

千萬不要搞混 JVM運行時數(shù)據(jù)區(qū)和JMM(Java memory modle)的關(guān)系

而JAVA對象布局在我的一篇文章中有講過他們是不同的概念
JAVA對象布局

下一篇我們來研究JMM(java的內(nèi)存模型)和Java中的逃逸分析,以及多線程編程延伸.
請持續(xù)關(guān)注公眾號:JAVA寶典

關(guān)注公眾號:java寶典

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

相關(guān)閱讀更多精彩內(nèi)容

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