您好,登錄后才能下訂單哦!
本篇內容主要講解“從計算機組成的視角認識JVM的內存分配在HotSpot虛擬機上的實現方法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“從計算機組成的視角認識JVM的內存分配在HotSpot虛擬機上的實現方法”吧!
從上圖可以看出,JDK8版本中,狹義上的JVM內存模型分為:程序計數器、本地方法棧、虛擬機棧和堆,其中前三者為線程私有的,最后的堆是線程共有的(所以GC主要發生在堆上)。廣義上的JVM內存模型,還包含一部分的本地內存,本地內存又可分為元空間與直接內存。
線程私有
未定義任何OutOfMemoryError異常
程序計數器,簡稱PC Register,它與CPU的寄存器還有本質上的區別,JVM的程序計數器不是獨立的硬件,只是一塊很小的內存空間,里面存放的就是當前Java線程正在執行的JVM指令對方法起始的偏移量。程序計數器只有在當前java線程執行的是java方法時才會有值,在執行本地方法時,其值為:undefined。
從計算機組成的角度來說,程序計數器中存放的就是一個整數,代表的就是當前正在執行的JVM指令是相對于方法的開始向后偏移的第幾個指令。
為什么程序計數器也叫做行號指示器
在反編譯class文件的時候,為了好越多,反編譯器會對反編譯出來的指令進行格式化,格式化后的就是一行為一條指令,而且每一行的前面還有一個看起來像是“行號”的東西,而程序計數器中存儲的就是這個“行號”,所以又成為“行號指示器”。但實際上這個看起來像是“行號”的東西實質上就是指令的偏移量。
本地方法棧本質上與虛擬機棧類似,但其是用于運行Native方法的。具體不做過多的介紹。
線程私有
定義有OutOfMemoryError異常
虛擬機棧是Java虛擬機的重要組成部分,方法的運行就依靠于它。一個方法調用在虛擬機棧中就是一個“棧幀”,棧底就是main方法的棧幀。棧幀主要由局部變量表、操作數棧、動態鏈接、返回地址(也叫方法出口)以及其它的附加信息組成。java文件中的一個方法調用,就對應著虛擬機棧中的一次入棧與出棧。
局部變量表是當前方法的入參與方法內的局部變量的存儲空間,局部變量表的容量在class文件編譯時根據方法的max_locals屬性就確定了最大容量。局部變量表的基本組成單位是“變量槽”(Variable Slot,一般簡稱Slot)。Java虛擬機規范中規定了“一個變量槽占用32位的空間”,32位的空間可以涵蓋Java絕大部分的基本數據類型,其余的(例如:long和double)一個變量槽存不下的就用兩個,但是程序在編譯期間會檢查,如果需要一次性訪問兩個變量槽(例如:獨讀取一個double類型的值)時,不允許采用任何方式只讀取其中的一個,萬一有發生這種情況則在編譯階段就報錯。
虛擬機通過索引定位的方式來使用局部變量表,但實際使用中索引的起始是從1開始,局部變量表的0號索引位存儲的就是this關鍵對當前方法所屬對象實例的引用,即堆空間中的一個內存地址。
從計算機組成的角度來說,局部變量表就是內存中的一塊兒確定大小的連續的空間,其所占用的字節空間大小是32或64的整數倍。
int i = 1 + 2;
例如上面的這段代碼,操作數棧中存儲的就是1和2這兩個數以及它們相加的結果3這個數據,但隨著程序的運行,1和2這兩個數會先后入棧,這兩次的入棧操作,對應著程序計數器中記錄的內容已經變化的了兩次(第一次是數字“1”入棧,第二次是數字“2”入棧,當程序計數器繼續記錄下一個指令的偏移量時,操作數棧中已經入棧的數字“1”和“2”會出棧,然后這兩個數相加的結果數字“3”會入棧。
從計算機組成的角度來說,操作數棧是一塊物理上不連續但邏輯上連續的內存空間,其所占用的字節空間大小是32或64的整數倍。
動態連接,也叫reference,所存儲的內容為了標示當前棧幀屬于那個方法,存儲的就是執行運行時常量池中該棧幀所屬方法的引用。
從計算機組成的角度來說,動態連接部分存儲的是一個內存地址。
返回地址中記錄的信息就是上一個方法棧幀在調用此方法時所對應的程序計數器的值,為了在當前方法執行結束(正常執行完畢結束或遇到異常中途退出結束)時,恢復調用當前這個方法的方法繼續運行。
返回地址中的值,在方法正常結束(未拋異常的執行完畢或拋出了異常但在當前方法中已經catch掉了)時用于恢復上一個方法的執行;當方法異常結束(拋出了異常沒有catch住)時,異常處理器在構造異常實例的時候,會使用便利異常堆棧,使用返回地址中的信息構建“異常堆棧”。
從計算機組成的角度來說,返回地址中存儲的內容和程序計數器中記錄的一樣,是一個整數。
通過對象實例調用方法時發生了什么
首先通過對象的元數據引用,找到對象的元數據,并找到對應的方法。
然后創建新的虛擬機棧的棧幀,將當前的程序計數器的內容復制到新棧幀的返回地址中,在新棧幀的動態連接中>寫入當前方法的引用。
如果新的方法需要入參,且入參的值正好位于當前方法棧幀操作數棧的頂部,則將操作數棧頂部的數據作為新方法棧幀的局部變量表的一部分(可能是復制數據,可能是內存地址共享,取決于具體的虛擬機實現)。
將程序計數器清空,開始新方法的執行。
線程共有
定義有OutOfMemoryError異常
Object obj = new Object();
堆是JVM中占用內存空間最大的區域,也是GC管理器最關注的區域。堆中存放的就是對象實例,以上面的代碼為例,“new Object()“所開辟的內存空間,主要就是在堆中。
對象實例首先分配在新生代的Eden區,如果Eden區域沒有足夠的空間可以容納新的對象實例,會觸發一次Minor GC,還在繼續用的對象會被轉移進Survivor,當存活超過一等的Minor GC次數,即對象年齡達到一定值后,會轉移入老年代。
從計算機組成的角度來說,堆中存放的就是程序運行中產生的對象及其全部變量等信息。
元數據區是在JDK8中新加的內容,是對Java虛擬機規范中的“方法區”描述的實現。JDK7及其以前,是使用“永久代”來實現方法區的,但是實際使用中發現來GC管理、性能上存在問題,且Oracle為了將Hotspot與JRockit整合,組裝兩者的長處,所以改用元數據區來實現方法區。
從計算機組成的角度來說,元數據區中存儲的就是class文件的信息,以及將class中的一些符號引用解析后的直接引用內存地址信息。
直接內存,在JDK1.4引入NIO后,基于NIO的MMap所使用的內存,就位于直接內存中。
從計算機組成的角度來說,直接內存中存儲的是數據內容。
使用代碼不停的創建新的字符串,得到結果如下兩圖,堆空間在發生變化,但元數據區幾乎沒有變化。可以得出結論:字符串常量池位于堆中。
到此,相信大家對“從計算機組成的視角認識JVM的內存分配在HotSpot虛擬機上的實現方法”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。