溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

從計(jì)算機(jī)組成的視角認(rèn)識JVM的內(nèi)存分配在HotSpot虛擬機(jī)上的實(shí)現(xiàn)方法

發(fā)布時(shí)間:2021-10-20 10:27:11 來源:億速云 閱讀:139 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“從計(jì)算機(jī)組成的視角認(rèn)識JVM的內(nèi)存分配在HotSpot虛擬機(jī)上的實(shí)現(xiàn)方法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“從計(jì)算機(jī)組成的視角認(rèn)識JVM的內(nèi)存分配在HotSpot虛擬機(jī)上的實(shí)現(xiàn)方法”吧!

從計(jì)算機(jī)組成的視角認(rèn)識JVM的內(nèi)存分配在HotSpot虛擬機(jī)上的實(shí)現(xiàn)方法

從上圖可以看出,JDK8版本中,狹義上的JVM內(nèi)存模型分為:程序計(jì)數(shù)器、本地方法棧、虛擬機(jī)棧和堆,其中前三者為線程私有的,最后的堆是線程共有的(所以GC主要發(fā)生在堆上)。廣義上的JVM內(nèi)存模型,還包含一部分的本地內(nèi)存,本地內(nèi)存又可分為元空間與直接內(nèi)存。

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

  1. 線程私有

  2. 未定義任何OutOfMemoryError異常

程序計(jì)數(shù)器,簡稱PC Register,它與CPU的寄存器還有本質(zhì)上的區(qū)別,JVM的程序計(jì)數(shù)器不是獨(dú)立的硬件,只是一塊很小的內(nèi)存空間,里面存放的就是當(dāng)前Java線程正在執(zhí)行的JVM指令對方法起始的偏移量。程序計(jì)數(shù)器只有在當(dāng)前java線程執(zhí)行的是java方法時(shí)才會(huì)有值,在執(zhí)行本地方法時(shí),其值為:undefined。

從計(jì)算機(jī)組成的角度來說,程序計(jì)數(shù)器中存放的就是一個(gè)整數(shù),代表的就是當(dāng)前正在執(zhí)行的JVM指令是相對于方法的開始向后偏移的第幾個(gè)指令。

為什么程序計(jì)數(shù)器也叫做行號指示器

在反編譯class文件的時(shí)候,為了好越多,反編譯器會(huì)對反編譯出來的指令進(jìn)行格式化,格式化后的就是一行為一條指令,而且每一行的前面還有一個(gè)看起來像是“行號”的東西,而程序計(jì)數(shù)器中存儲(chǔ)的就是這個(gè)“行號”,所以又成為“行號指示器”。但實(shí)際上這個(gè)看起來像是“行號”的東西實(shí)質(zhì)上就是指令的偏移量。

2. 本地方法棧 Native Method Stack

本地方法棧本質(zhì)上與虛擬機(jī)棧類似,但其是用于運(yùn)行Native方法的。具體不做過多的介紹。

3. 虛擬機(jī)棧 VM Stack

  1. 線程私有

  2. 定義有OutOfMemoryError異常

虛擬機(jī)棧是Java虛擬機(jī)的重要組成部分,方法的運(yùn)行就依靠于它。一個(gè)方法調(diào)用在虛擬機(jī)棧中就是一個(gè)“棧幀”,棧底就是main方法的棧幀。棧幀主要由局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、返回地址(也叫方法出口)以及其它的附加信息組成。java文件中的一個(gè)方法調(diào)用,就對應(yīng)著虛擬機(jī)棧中的一次入棧與出棧。

3.1 局部變量表 Local Variable Table

局部變量表是當(dāng)前方法的入?yún)⑴c方法內(nèi)的局部變量的存儲(chǔ)空間,局部變量表的容量在class文件編譯時(shí)根據(jù)方法的max_locals屬性就確定了最大容量。局部變量表的基本組成單位是“變量槽”(Variable Slot,一般簡稱Slot)。Java虛擬機(jī)規(guī)范中規(guī)定了“一個(gè)變量槽占用32位的空間”,32位的空間可以涵蓋Java絕大部分的基本數(shù)據(jù)類型,其余的(例如:long和double)一個(gè)變量槽存不下的就用兩個(gè),但是程序在編譯期間會(huì)檢查,如果需要一次性訪問兩個(gè)變量槽(例如:獨(dú)讀取一個(gè)double類型的值)時(shí),不允許采用任何方式只讀取其中的一個(gè),萬一有發(fā)生這種情況則在編譯階段就報(bào)錯(cuò)。

虛擬機(jī)通過索引定位的方式來使用局部變量表,但實(shí)際使用中索引的起始是從1開始,局部變量表的0號索引位存儲(chǔ)的就是this關(guān)鍵對當(dāng)前方法所屬對象實(shí)例的引用,即堆空間中的一個(gè)內(nèi)存地址。

從計(jì)算機(jī)組成的角度來說,局部變量表就是內(nèi)存中的一塊兒確定大小的連續(xù)的空間,其所占用的字節(jié)空間大小是32或64的整數(shù)倍。

3.2 操作數(shù)棧 Operand Stack

int i = 1 + 2;

例如上面的這段代碼,操作數(shù)棧中存儲(chǔ)的就是1和2這兩個(gè)數(shù)以及它們相加的結(jié)果3這個(gè)數(shù)據(jù),但隨著程序的運(yùn)行,1和2這兩個(gè)數(shù)會(huì)先后入棧,這兩次的入棧操作,對應(yīng)著程序計(jì)數(shù)器中記錄的內(nèi)容已經(jīng)變化的了兩次(第一次是數(shù)字“1”入棧,第二次是數(shù)字“2”入棧,當(dāng)程序計(jì)數(shù)器繼續(xù)記錄下一個(gè)指令的偏移量時(shí),操作數(shù)棧中已經(jīng)入棧的數(shù)字“1”和“2”會(huì)出棧,然后這兩個(gè)數(shù)相加的結(jié)果數(shù)字“3”會(huì)入棧。

從計(jì)算機(jī)組成的角度來說,操作數(shù)棧是一塊物理上不連續(xù)但邏輯上連續(xù)的內(nèi)存空間,其所占用的字節(jié)空間大小是32或64的整數(shù)倍。

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

動(dòng)態(tài)連接,也叫reference,所存儲(chǔ)的內(nèi)容為了標(biāo)示當(dāng)前棧幀屬于那個(gè)方法,存儲(chǔ)的就是執(zhí)行運(yùn)行時(shí)常量池中該棧幀所屬方法的引用。

從計(jì)算機(jī)組成的角度來說,動(dòng)態(tài)連接部分存儲(chǔ)的是一個(gè)內(nèi)存地址。

3.4 返回地址 Return Address

返回地址中記錄的信息就是上一個(gè)方法棧幀在調(diào)用此方法時(shí)所對應(yīng)的程序計(jì)數(shù)器的值,為了在當(dāng)前方法執(zhí)行結(jié)束(正常執(zhí)行完畢結(jié)束或遇到異常中途退出結(jié)束)時(shí),恢復(fù)調(diào)用當(dāng)前這個(gè)方法的方法繼續(xù)運(yùn)行。

返回地址中的值,在方法正常結(jié)束(未拋異常的執(zhí)行完畢或拋出了異常但在當(dāng)前方法中已經(jīng)catch掉了)時(shí)用于恢復(fù)上一個(gè)方法的執(zhí)行;當(dāng)方法異常結(jié)束(拋出了異常沒有catch?。r(shí),異常處理器在構(gòu)造異常實(shí)例的時(shí)候,會(huì)使用便利異常堆棧,使用返回地址中的信息構(gòu)建“異常堆?!?。

從計(jì)算機(jī)組成的角度來說,返回地址中存儲(chǔ)的內(nèi)容和程序計(jì)數(shù)器中記錄的一樣,是一個(gè)整數(shù)。

通過對象實(shí)例調(diào)用方法時(shí)發(fā)生了什么
  1. 首先通過對象的元數(shù)據(jù)引用,找到對象的元數(shù)據(jù),并找到對應(yīng)的方法。

  2. 然后創(chuàng)建新的虛擬機(jī)棧的棧幀,將當(dāng)前的程序計(jì)數(shù)器的內(nèi)容復(fù)制到新棧幀的返回地址中,在新棧幀的動(dòng)態(tài)連接中>寫入當(dāng)前方法的引用。

  3. 如果新的方法需要入?yún)?,且入?yún)⒌闹嫡梦挥诋?dāng)前方法棧幀操作數(shù)棧的頂部,則將操作數(shù)棧頂部的數(shù)據(jù)作為新方法棧幀的局部變量表的一部分(可能是復(fù)制數(shù)據(jù),可能是內(nèi)存地址共享,取決于具體的虛擬機(jī)實(shí)現(xiàn))。

  4. 將程序計(jì)數(shù)器清空,開始新方法的執(zhí)行。

4. 堆 Heap

  1. 線程共有

  2. 定義有OutOfMemoryError異常

Object obj = new Object();

堆是JVM中占用內(nèi)存空間最大的區(qū)域,也是GC管理器最關(guān)注的區(qū)域。堆中存放的就是對象實(shí)例,以上面的代碼為例,“new Object()“所開辟的內(nèi)存空間,主要就是在堆中。

對象實(shí)例首先分配在新生代的Eden區(qū),如果Eden區(qū)域沒有足夠的空間可以容納新的對象實(shí)例,會(huì)觸發(fā)一次Minor GC,還在繼續(xù)用的對象會(huì)被轉(zhuǎn)移進(jìn)Survivor,當(dāng)存活超過一等的Minor GC次數(shù),即對象年齡達(dá)到一定值后,會(huì)轉(zhuǎn)移入老年代。

從計(jì)算機(jī)組成的角度來說,堆中存放的就是程序運(yùn)行中產(chǎn)生的對象及其全部變量等信息。

5. 元數(shù)據(jù)區(qū) Meta Space

元數(shù)據(jù)區(qū)是在JDK8中新加的內(nèi)容,是對Java虛擬機(jī)規(guī)范中的“方法區(qū)”描述的實(shí)現(xiàn)。JDK7及其以前,是使用“永久代”來實(shí)現(xiàn)方法區(qū)的,但是實(shí)際使用中發(fā)現(xiàn)來GC管理、性能上存在問題,且Oracle為了將Hotspot與JRockit整合,組裝兩者的長處,所以改用元數(shù)據(jù)區(qū)來實(shí)現(xiàn)方法區(qū)。

從計(jì)算機(jī)組成的角度來說,元數(shù)據(jù)區(qū)中存儲(chǔ)的就是class文件的信息,以及將class中的一些符號引用解析后的直接引用內(nèi)存地址信息。

6. 直接內(nèi)存 Direct Memory

直接內(nèi)存,在JDK1.4引入NIO后,基于NIO的MMap所使用的內(nèi)存,就位于直接內(nèi)存中。

從計(jì)算機(jī)組成的角度來說,直接內(nèi)存中存儲(chǔ)的是數(shù)據(jù)內(nèi)容。

測試

字符串常量池是位于堆中還是位于元數(shù)據(jù)區(qū)中

使用代碼不停的創(chuàng)建新的字符串,得到結(jié)果如下兩圖,堆空間在發(fā)生變化,但元數(shù)據(jù)區(qū)幾乎沒有變化??梢缘贸鼋Y(jié)論:字符串常量池位于堆中。 從計(jì)算機(jī)組成的視角認(rèn)識JVM的內(nèi)存分配在HotSpot虛擬機(jī)上的實(shí)現(xiàn)方法 從計(jì)算機(jī)組成的視角認(rèn)識JVM的內(nèi)存分配在HotSpot虛擬機(jī)上的實(shí)現(xiàn)方法

到此,相信大家對“從計(jì)算機(jī)組成的視角認(rèn)識JVM的內(nèi)存分配在HotSpot虛擬機(jī)上的實(shí)現(xiàn)方法”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI