本篇內(nèi)容主要講解“從計算機組成的視角認識JVM的內(nèi)存分配在HotSpot虛擬機上的實現(xiàn)方法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“從計算機組成的視角認識JVM的內(nèi)存分配在HotSpot虛擬機上的實現(xiàn)方法”吧!
創(chuàng)新互聯(lián)主要從事成都網(wǎng)站制作、成都網(wǎng)站設計、網(wǎng)頁設計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務。立足成都服務永善,十載網(wǎng)站建設經(jīng)驗,價格優(yōu)惠、服務專業(yè),歡迎來電咨詢建站服務:18980820575
從上圖可以看出,JDK8版本中,狹義上的JVM內(nèi)存模型分為:程序計數(shù)器、本地方法棧、虛擬機棧和堆,其中前三者為線程私有的,最后的堆是線程共有的(所以GC主要發(fā)生在堆上)。廣義上的JVM內(nèi)存模型,還包含一部分的本地內(nèi)存,本地內(nèi)存又可分為元空間與直接內(nèi)存。
線程私有
未定義任何OutOfMemoryError異常
程序計數(shù)器,簡稱PC Register,它與CPU的寄存器還有本質上的區(qū)別,JVM的程序計數(shù)器不是獨立的硬件,只是一塊很小的內(nèi)存空間,里面存放的就是當前Java線程正在執(zhí)行的JVM指令對方法起始的偏移量。程序計數(shù)器只有在當前java線程執(zhí)行的是java方法時才會有值,在執(zhí)行本地方法時,其值為:undefined。
從計算機組成的角度來說,程序計數(shù)器中存放的就是一個整數(shù),代表的就是當前正在執(zhí)行的JVM指令是相對于方法的開始向后偏移的第幾個指令。
為什么程序計數(shù)器也叫做行號指示器
在反編譯class文件的時候,為了好越多,反編譯器會對反編譯出來的指令進行格式化,格式化后的就是一行為一條指令,而且每一行的前面還有一個看起來像是“行號”的東西,而程序計數(shù)器中存儲的就是這個“行號”,所以又成為“行號指示器”。但實際上這個看起來像是“行號”的東西實質上就是指令的偏移量。
本地方法棧本質上與虛擬機棧類似,但其是用于運行Native方法的。具體不做過多的介紹。
線程私有
定義有OutOfMemoryError異常
虛擬機棧是Java虛擬機的重要組成部分,方法的運行就依靠于它。一個方法調用在虛擬機棧中就是一個“棧幀”,棧底就是main方法的棧幀。棧幀主要由局部變量表、操作數(shù)棧、動態(tài)鏈接、返回地址(也叫方法出口)以及其它的附加信息組成。java文件中的一個方法調用,就對應著虛擬機棧中的一次入棧與出棧。
局部變量表是當前方法的入?yún)⑴c方法內(nèi)的局部變量的存儲空間,局部變量表的容量在class文件編譯時根據(jù)方法的max_locals屬性就確定了最大容量。局部變量表的基本組成單位是“變量槽”(Variable Slot,一般簡稱Slot)。Java虛擬機規(guī)范中規(guī)定了“一個變量槽占用32位的空間”,32位的空間可以涵蓋Java絕大部分的基本數(shù)據(jù)類型,其余的(例如:long和double)一個變量槽存不下的就用兩個,但是程序在編譯期間會檢查,如果需要一次性訪問兩個變量槽(例如:獨讀取一個double類型的值)時,不允許采用任何方式只讀取其中的一個,萬一有發(fā)生這種情況則在編譯階段就報錯。
虛擬機通過索引定位的方式來使用局部變量表,但實際使用中索引的起始是從1開始,局部變量表的0號索引位存儲的就是this關鍵對當前方法所屬對象實例的引用,即堆空間中的一個內(nèi)存地址。
從計算機組成的角度來說,局部變量表就是內(nèi)存中的一塊兒確定大小的連續(xù)的空間,其所占用的字節(jié)空間大小是32或64的整數(shù)倍。
int i = 1 + 2;
例如上面的這段代碼,操作數(shù)棧中存儲的就是1和2這兩個數(shù)以及它們相加的結果3這個數(shù)據(jù),但隨著程序的運行,1和2這兩個數(shù)會先后入棧,這兩次的入棧操作,對應著程序計數(shù)器中記錄的內(nèi)容已經(jīng)變化的了兩次(第一次是數(shù)字“1”入棧,第二次是數(shù)字“2”入棧,當程序計數(shù)器繼續(xù)記錄下一個指令的偏移量時,操作數(shù)棧中已經(jīng)入棧的數(shù)字“1”和“2”會出棧,然后這兩個數(shù)相加的結果數(shù)字“3”會入棧。
從計算機組成的角度來說,操作數(shù)棧是一塊物理上不連續(xù)但邏輯上連續(xù)的內(nèi)存空間,其所占用的字節(jié)空間大小是32或64的整數(shù)倍。
動態(tài)連接,也叫reference,所存儲的內(nèi)容為了標示當前棧幀屬于那個方法,存儲的就是執(zhí)行運行時常量池中該棧幀所屬方法的引用。
從計算機組成的角度來說,動態(tài)連接部分存儲的是一個內(nèi)存地址。
返回地址中記錄的信息就是上一個方法棧幀在調用此方法時所對應的程序計數(shù)器的值,為了在當前方法執(zhí)行結束(正常執(zhí)行完畢結束或遇到異常中途退出結束)時,恢復調用當前這個方法的方法繼續(xù)運行。
返回地址中的值,在方法正常結束(未拋異常的執(zhí)行完畢或拋出了異常但在當前方法中已經(jīng)catch掉了)時用于恢復上一個方法的執(zhí)行;當方法異常結束(拋出了異常沒有catch?。r,異常處理器在構造異常實例的時候,會使用便利異常堆棧,使用返回地址中的信息構建“異常堆?!薄?/p>
從計算機組成的角度來說,返回地址中存儲的內(nèi)容和程序計數(shù)器中記錄的一樣,是一個整數(shù)。
通過對象實例調用方法時發(fā)生了什么
首先通過對象的元數(shù)據(jù)引用,找到對象的元數(shù)據(jù),并找到對應的方法。
然后創(chuàng)建新的虛擬機棧的棧幀,將當前的程序計數(shù)器的內(nèi)容復制到新棧幀的返回地址中,在新棧幀的動態(tài)連接中>寫入當前方法的引用。
如果新的方法需要入?yún)?,且入?yún)⒌闹嫡梦挥诋斍胺椒僮鲾?shù)棧的頂部,則將操作數(shù)棧頂部的數(shù)據(jù)作為新方法棧幀的局部變量表的一部分(可能是復制數(shù)據(jù),可能是內(nèi)存地址共享,取決于具體的虛擬機實現(xiàn))。
將程序計數(shù)器清空,開始新方法的執(zhí)行。
線程共有
定義有OutOfMemoryError異常
Object obj = new Object();
堆是JVM中占用內(nèi)存空間最大的區(qū)域,也是GC管理器最關注的區(qū)域。堆中存放的就是對象實例,以上面的代碼為例,“new Object()“所開辟的內(nèi)存空間,主要就是在堆中。
對象實例首先分配在新生代的Eden區(qū),如果Eden區(qū)域沒有足夠的空間可以容納新的對象實例,會觸發(fā)一次Minor GC,還在繼續(xù)用的對象會被轉移進Survivor,當存活超過一等的Minor GC次數(shù),即對象年齡達到一定值后,會轉移入老年代。
從計算機組成的角度來說,堆中存放的就是程序運行中產(chǎn)生的對象及其全部變量等信息。
元數(shù)據(jù)區(qū)是在JDK8中新加的內(nèi)容,是對Java虛擬機規(guī)范中的“方法區(qū)”描述的實現(xiàn)。JDK7及其以前,是使用“永久代”來實現(xiàn)方法區(qū)的,但是實際使用中發(fā)現(xiàn)來GC管理、性能上存在問題,且Oracle為了將Hotspot與JRockit整合,組裝兩者的長處,所以改用元數(shù)據(jù)區(qū)來實現(xiàn)方法區(qū)。
從計算機組成的角度來說,元數(shù)據(jù)區(qū)中存儲的就是class文件的信息,以及將class中的一些符號引用解析后的直接引用內(nèi)存地址信息。
直接內(nèi)存,在JDK1.4引入NIO后,基于NIO的MMap所使用的內(nèi)存,就位于直接內(nèi)存中。
從計算機組成的角度來說,直接內(nèi)存中存儲的是數(shù)據(jù)內(nèi)容。
使用代碼不停的創(chuàng)建新的字符串,得到結果如下兩圖,堆空間在發(fā)生變化,但元數(shù)據(jù)區(qū)幾乎沒有變化??梢缘贸鼋Y論:字符串常量池位于堆中。
到此,相信大家對“從計算機組成的視角認識JVM的內(nèi)存分配在HotSpot虛擬機上的實現(xiàn)方法”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關內(nèi)容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!