一、虛擬機內(nèi)存結(jié)構(gòu)
在jvm虛擬機運行程序的過程中,會管理著一塊內(nèi)存區(qū)域,稱為運行時數(shù)據(jù)區(qū)。
在運行時數(shù)據(jù)區(qū)包含一下幾塊區(qū)域:
- 程序計數(shù)器
- 虛擬機棧
- 本地方法棧
- 堆區(qū)
- 方法區(qū)

二、程序計數(shù)器
程序計數(shù)器也可以叫做PC寄存器,它的作用是用來存儲指向下一條指令的地址,也就是即將要執(zhí)行的指令代碼,由執(zhí)行引擎來讀取下一條指令。Java程序中分支,循環(huán),跳轉(zhuǎn),異常處理,線程恢復等都需要依賴這個PC寄存器來完成。
特點:
- 內(nèi)存占用很小,幾乎可以忽略不記,也是運行速度最快的存儲區(qū)域。
- 每個線程都擁有自己的程序計數(shù)器,是線程私有的。
- 生命周期與線程相同。
- 是虛擬機中唯一一個沒有OutOfMemoryError的內(nèi)存區(qū)域。
那么什么是指令地址?我們來舉個例子:
public class PcRegister {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = a+b;
}
}
上面的代碼,我們編譯之后會生成.class文件,然后通過javap -v命令來查看這個字節(jié)碼內(nèi)容,如圖所示,圈出的部分就是指令地址,后面的就是具體的指令。

三、虛擬機棧
又稱Java棧,作用是保存方法的局部變量,部分結(jié)果,并參與方法的調(diào)用和返回。
特點:
- 每個線程都有自己的棧,線程私有。
- 生命周期與線程相同。
- 每個方法執(zhí)行時,都會創(chuàng)建一個棧幀。
- 棧幀又包含:局部變量表,操作數(shù)棧,動態(tài)鏈接,方法返回地址和一些附加信息。
異常:
Java虛擬機規(guī)范允許Java棧的大小時動態(tài)的或者固定不變的。
- 如果是固定大小的虛擬機棧,當分配的棧容量超過虛擬機棧的最大容量時,會拋出StackOverflowError。
- 如果時動態(tài)大小的虛擬機棧,當動態(tài)擴展時無法申請到足夠的內(nèi)存時,會拋出OutofMemoryError。
四、本地方法棧
虛擬機棧用于管理Java方法的調(diào)用,而本地方法棧用于管理本地方法的調(diào)用。
特點:
- 每個線程都有自己的本地方法棧,線程私有。
- 生命周期與線程相同。
- 異常方面與虛擬機棧相同。
五、堆區(qū)
一個Jvm實例中,只存在一個堆內(nèi)存,堆是Jvm內(nèi)存中最核心的區(qū)域,也是垃圾回收器管理的主要的區(qū)域,堆區(qū)在Jvm啟動時被創(chuàng)建,其空間大小被確定,是Jvm管理最大的一塊內(nèi)存空間,主要作用是用來存儲Java對象實例。
特點:
- Jvm中占內(nèi)存最大。
- Jvm中所有線程共享堆。堆中還可以劃分線程私有的緩沖區(qū)。
- 方法結(jié)束后,堆中的對象不會馬上被移除,需要在垃圾回收時才會移除。
- 由于收集器基本都采用分代收集算法,所以堆內(nèi)存又細分為:新生代和老年代。
新生代:年輕代又分為一個Eden空間,Survivor0和Survivor1空間(有時也叫from,to區(qū))新創(chuàng)建的對象就是先進入新生代。
老年代:大對象直接進入老年代。如果新生代中的對象,經(jīng)過一段時間還未被回收就會進入老年代。
異常:
當堆中沒有足夠的內(nèi)存完成對象的分配時,會拋出OutOfMemoryError。
六、方法區(qū)
方法區(qū)和堆區(qū)相同的是,都是各個線程共享的內(nèi)存區(qū)域,jvm啟動時創(chuàng)建,關(guān)閉時釋放,并且它實際的物理地址空間和堆區(qū)一樣都可以是不連續(xù)的,大小也可以固定大小或可擴展,主要作用是存儲已被虛擬機加載的類的信息,方法信息,字段信息,常量,靜態(tài)變量,編譯后的代碼緩存等。
特點:
- Jvm中所有線程共享方法區(qū)。
異常:
方法區(qū)的大小決定了系統(tǒng)可以保存多少個類,如果定義了太多的類,導致方法區(qū)溢出,同樣會拋出OutOfMemoryError。