對于 JVM 運行時內(nèi)存布局,我們需要始終記住一點:我們即將介紹的這 5 塊內(nèi)容都是在 Java 虛擬機規(guī)范中定義的規(guī)則,這些規(guī)則只是描述了各個區(qū)域是負責(zé)做什么事情、存儲什么樣的數(shù)據(jù)、如何處理異常杰扫、是否允許線程間共享等,具體不同的虛擬機有不同的實現(xiàn)方式:
下面用一張圖片來描述一下Java文件被JVM加載的內(nèi)存的過程
程序計數(shù)器:
Java是多線程的,cpu可以在多個線程中分配執(zhí)行時間段膘掰。當(dāng)某一個線程被CPU掛起時章姓,需要記錄代碼已經(jīng)執(zhí)行到的位置,方便CPU重新執(zhí)行時,知道從哪里開始執(zhí)行凡伊。除了這個零渐,我們常用的分支、循環(huán)系忙、跳轉(zhuǎn)诵盼、異常處理等也需要依賴程序計數(shù)器。
它占用的空間比較小银还,沒有OOM觸發(fā)規(guī)則拦耐,是線程私有化數(shù)據(jù),生命周期跟隨線程见剩。
當(dāng)一個線程執(zhí)行java方法時,他記錄的是虛擬機字節(jié)碼的地址扫俺。如果執(zhí)行的是native方法苍苞,則計數(shù)器的值為空(Undefined)。
虛擬機棧
虛擬機棧是線程私有的狼纬,與線程的生命周期同步羹呵,Java虛擬機對這個區(qū)域定義了兩種異常規(guī)范:
StackOverflowError
:當(dāng)線程請求棧深度超出虛擬機棧所允許的深度時拋出。 OutOfMemoryError
:當(dāng) Java 虛擬機動態(tài)擴展到無法申請足夠內(nèi)存時拋出
JVM在每個方法執(zhí)行的時候疗琉,都會在虛擬機棧中創(chuàng)建一個棧幀冈欢。
棧幀(Stack Frame)
它是虛擬機棧進行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu),每一個線程在執(zhí)行某一個方法時盈简,都會為這個方法創(chuàng)建一個棧幀凑耻。
一個線程可以包含多個棧幀,每個棧幀包含局部變量表
柠贤,操作數(shù)棧
香浩,動態(tài)鏈接
,返回地址
等臼勉。
局部變量表
:表示的是變量值的儲存空間邻吭,方法內(nèi)部創(chuàng)建的變量和傳參都保存在局部變量表中。在java編譯成class文件的時候宴霸,就會在方法的code屬性表中的max_locals數(shù)據(jù)項中囱晴,確定該方法需要分配的最大局部變量表的容量。注意系統(tǒng)不會為局部變量賦值初始值瓢谢。
操作數(shù)棧(Operand Stack)
:也被稱為操作棧畸写,它是一個后入先出棧,在java編譯成class文件的時候恩闻,操作數(shù)棧的最大深度在編譯的時候也寫入code屬性表中的max_stacks數(shù)據(jù)項中艺糜。棧中的元素可以是任意java數(shù)據(jù)類型,當(dāng)一個方法開始執(zhí)行的時候,這個方法的操作數(shù)棧是空的破停,在方法的執(zhí)行過程中翅楼,會有各種字節(jié)碼指令被壓入棧和彈出操作數(shù)棧。
動態(tài)鏈接(Dynamic Linking)
:主要是為了支持在方法調(diào)用過程的動態(tài)鏈接真慢。在一個class文件中毅臊,一個方法要調(diào)用其他方法,需要將這些方法的符號引用轉(zhuǎn)換為其所在內(nèi)存地址的直接引用黑界,而符號引用存在于方法區(qū)中管嬉。
返回地址
:在方法退出后都需要返回到方法被調(diào)用的位置,程序才可以繼續(xù)執(zhí)行朗鸠,而這個返回地址就是幫助方法恢復(fù)上一層的執(zhí)行狀態(tài)蚯撩。
本地方法棧
和虛擬機棧基本相同烛占,只不過針對native方法胎挎。在有些虛擬機的實現(xiàn)中已經(jīng)將兩個合二為一了(比如HotSpot)。
堆(heap)
堆是JVM所管理內(nèi)存中最大的一塊忆家,該區(qū)域的唯一目的就是存放對象實例犹菇,幾乎所有的對象都是在堆中分配,所以它也是GC管理的主要區(qū)域芽卿,它是所有線程的共享區(qū)域揭芍。按照對象儲存時間的不同,堆的內(nèi)存又可以劃分為新生代和老年代卸例,其中新生代又被劃分為Eden和Survivor區(qū)
方法區(qū)
它是JVM規(guī)定的一塊運行時數(shù)據(jù)區(qū)称杨,該方法主要是存儲已經(jīng)被JVM加載的類信息(版本、字段币厕、方法列另、接口)、常量旦装、靜態(tài)變量页衙、即時編譯器編譯后的代碼和數(shù)據(jù),該區(qū)域和堆一樣阴绢,是被各個線程共享的區(qū)域店乐。
注意:方法區(qū)是jvm規(guī)范的一塊區(qū)域,是個概念呻袭,永久區(qū)是HotSpot在JDK1.7對方法區(qū)的實現(xiàn)眨八。但在JDK1.8移除了,用metaspace的實現(xiàn)方式左电。
用一張圖總結(jié):