java虛擬機(jī)中內(nèi)存劃分如下圖所示,下面具體談一下每一個部分页滚。
1.程序計(jì)數(shù)器
程序計(jì)數(shù)器是一個比較小的內(nèi)存區(qū)域萤彩,用于指示當(dāng)前線程所執(zhí)行的字節(jié)碼執(zhí)行到了第幾行村视,類似于匯編語言的程序計(jì)數(shù)器伊磺。每個程序計(jì)數(shù)器只用來記錄一個線程的行號盛正,所以它是線程私有(一個線程就有一個程序計(jì)數(shù)器)的。
如果程序執(zhí)行的是一個Java方法屑埋,則計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令地址豪筝;如果正在執(zhí)行的是一個本地(native,由C語言編寫完成)方法摘能,則計(jì)數(shù)器的值為Undefined续崖,由于程序計(jì)數(shù)器只是記錄當(dāng)前指令地址,所以不存在內(nèi)存溢出的情況团搞,因此严望,程序計(jì)數(shù)器也是所有JVM內(nèi)存區(qū)域中唯一一個沒有定義OutOfMemoryError的區(qū)域。
2.虛擬機(jī)棧
一個線程的每個方法在執(zhí)行的同時逻恐,都會創(chuàng)建一個棧幀(Statck Frame)像吻,棧幀中存儲的有局部變量表、操作站复隆、動態(tài)鏈接拨匆、方法出口等,當(dāng)方法被調(diào)用時挽拂,棧幀在JVM棧中入棧惭每,當(dāng)方法執(zhí)行完成時,棧幀出棧亏栈。
局部變量表中存儲著方法的相關(guān)局部變量台腥,包括各種基本數(shù)據(jù)類型,對象的引用绒北,返回地址等黎侈。在局部變量表中,只有l(wèi)ong和double類型會占用2個局部變量空間(Slot镇饮,對于32位機(jī)器蜓竹,一個Slot就是32個bit),其它都是1個Slot储藐。需要注意的是俱济,局部變量表是在編譯時就已經(jīng)確定好的,方法運(yùn)行所需要分配的空間在棧幀中是完全確定的钙勃,在方法的生命周期內(nèi)都不會改變蛛碌。
虛擬機(jī)棧中定義了兩種異常,如果線程調(diào)用的棧深度大于虛擬機(jī)允許的最大深度辖源,則拋出StatckOverFlowError(棧溢出)蔚携;不過多數(shù)Java虛擬機(jī)都允許動態(tài)擴(kuò)展虛擬機(jī)棧的大小(有少部分是固定長度的)希太,所以線程可以一直申請棧,直到內(nèi)存不足酝蜒,此時誊辉,會拋出OutOfMemoryError(內(nèi)存溢出)。
每個線程對應(yīng)著一個虛擬機(jī)棧亡脑,因此虛擬機(jī)棧也是線程私有的堕澄。
3.本地方法棧
本地方法棧在作用,運(yùn)行機(jī)制霉咨,異常類型等方面都與虛擬機(jī)棧相同蛙紫,唯一的區(qū)別是:虛擬機(jī)棧是執(zhí)行Java方法的,而本地方法棧是用來執(zhí)行native方法的途戒,在很多虛擬機(jī)中(如Sun的JDK默認(rèn)的HotSpot虛擬機(jī))坑傅,會將本地方法棧與虛擬機(jī)棧放在一起使用。
本地方法棧也是線程私有的喷斋。
4.堆區(qū)
在JVM所管理的內(nèi)存中唁毒,堆區(qū)是最大的一塊,堆區(qū)也是Java GC機(jī)制所管理的主要內(nèi)存區(qū)域继准,堆區(qū)由所有線程共享枉证,在虛擬機(jī)啟動時創(chuàng)建。堆區(qū)的存在是為了存儲對象實(shí)例和數(shù)組移必。而對象的引用變量存儲在棧中,當(dāng)沒有引用指向一個對象時毡鉴,java提供了一種垃圾回收機(jī)制自動進(jìn)行處理崔泵。
5.方法區(qū)
方法區(qū)(Method Area):在Java虛擬機(jī)規(guī)范中,將方法區(qū)作為堆的一個邏輯部分來對待猪瞬,但事實(shí)上憎瘸,方法區(qū)并不是堆(Non-Heap);方法區(qū)是各個線程共享的區(qū)域陈瘦,用于存儲已經(jīng)被虛擬機(jī)加載的類信息(即加載類時需要加載的信息幌甘,包括版本、field痊项、方法锅风、接口等信息)、final常量鞍泉、靜態(tài)變量皱埠、編譯器即時編譯的代碼等。
方法區(qū)在物理上也不需要是連續(xù)的咖驮,可以選擇固定大小或可擴(kuò)展大小边器,并且方法區(qū)比堆還多了一個限制:可以選擇是否執(zhí)行垃圾收集训枢。一般的,方法區(qū)上執(zhí)行的垃圾收集是很少的忘巧,這也是方法區(qū)被稱為永久代的原因之一(HotSpot)恒界,但這也不代表著在方法區(qū)上完全沒有垃圾收集,其上的垃圾收集主要是針對常量池的內(nèi)存回收和對已加載類的卸載砚嘴。
運(yùn)行時常量池(Runtime Constant Pool)是方法區(qū)的一部分仗处,用于存儲編譯期就生成的字面常量、符號引用枣宫、翻譯出來的直接引用(符號引用就是編碼是用字符串表示某個變量婆誓、接口的位置,直接引用就是根據(jù)符號引用翻譯出來的地址也颤,將在類鏈接階段完成翻譯)洋幻;運(yùn)行時常量池除了存儲編譯期常量外,也可以存儲在運(yùn)行時間產(chǎn)生的常量(比如String類的intern()方法翅娶,作用是String維護(hù)了一個常量池文留,如果調(diào)用的字符“abc”已經(jīng)在常量池中,則返回池中的字符串地址竭沫,否則燥翅,新建一個常量加入池中,并返回地址)蜕提。
一般來說森书,一個Java的引用訪問涉及到3個內(nèi)存區(qū)域:JVM棧,堆谎势,方法區(qū)凛膏。以最簡單的本地變量引用:Object obj = new Object()為例:
Object obj表示一個本地引用,存儲在JVM棧的本地變量表中脏榆,表示一個reference類型數(shù)據(jù)猖毫;
new Object()作為實(shí)例對象數(shù)據(jù)存儲在堆中;
堆中還記錄了Object類的類型信息(接口须喂、方法吁断、field、對象類型等)的地址坞生,這些地址所執(zhí)行的數(shù)據(jù)存儲在方法區(qū)中仔役;