Java語言和虛擬機(jī)規(guī)范官方文檔:https://docs.oracle.com/javase/specs/index.html
概述
Java虛擬機(jī)在執(zhí)行程序的過程中,會(huì)把它管理的內(nèi)存分為不同的數(shù)據(jù)區(qū)域恋追。其中一些數(shù)據(jù)區(qū)域是在Java虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建的隙疚,僅在Java虛擬機(jī)退出時(shí)才被銷毀。有些數(shù)據(jù)區(qū)域是每個(gè)線程私有的搬瑰。在創(chuàng)建線程時(shí)創(chuàng)建每個(gè)線程的數(shù)據(jù)區(qū)域,并在線程退出時(shí)銷毀每個(gè)數(shù)據(jù)區(qū)域。如圖所示:
程序計(jì)數(shù)器
程序計(jì)數(shù)器可以看作當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器哪痰。
每個(gè)線程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器,各個(gè)線程之間的計(jì)數(shù)器互不影響?yīng)毩⒋鎯?chǔ)久妆,可以說這類內(nèi)存區(qū)域是線程私有的晌杰。
如果線程正在執(zhí)行的是Java方法,那么這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址筷弦。
如果線程正在執(zhí)行的是Native方法肋演,那么這個(gè)計(jì)數(shù)器的值是Undefined。
此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutofMemoryError情況的區(qū)域奸笤。
Java虛擬機(jī)棧
Java虛擬機(jī)棧也是線程私有的惋啃,它是為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù)。
每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表监右、操作數(shù)棧边灭、動(dòng)態(tài)鏈接、方法出口等信息健盒。
每個(gè)方法從調(diào)用到執(zhí)行完成過程中绒瘦,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過程称簿。
局部變量表存放了編譯期可知的8種數(shù)據(jù)類型、對(duì)象引用和returnAddress類型(指向了一條字節(jié)碼指令地址)惰帽。
如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度憨降,將拋出StackOverflowError異常。
如果虛擬機(jī)椄眯铮可以動(dòng)態(tài)擴(kuò)展授药,擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存,就會(huì)拋出OutOfMemoryError異常呜魄。
本地方法棧
本地方法棧是為虛擬機(jī)適用到的Native方法服務(wù)的悔叽。有的虛擬機(jī)(譬如sun HotSpot)直接就把本地方法棧和虛擬機(jī)棧合二為一。它拋出的異常與虛擬機(jī)棧類似爵嗅。
Java堆
對(duì)大多數(shù)應(yīng)用來說娇澎,Java堆是虛擬機(jī)管理的內(nèi)存中最大的一塊區(qū)域,是被所有線程共享的區(qū)域睹晒。官方文檔中寫道
The heap is the run-time data area from which memory for all class instances and arrays is allocated.
即所有的對(duì)象實(shí)例和數(shù)組都要在堆上分配趟庄。但隨著JIT編譯器發(fā)展,這并非絕對(duì)的伪很。
Java堆書垃圾回收的主要區(qū)域戚啥。它還可以細(xì)分為:新生代和老年代;再細(xì)致一點(diǎn)有Eden空間是掰、From Survivor空間虑鼎、To Survivor空間。
如果在堆中沒有內(nèi)存可用键痛,且堆無法再擴(kuò)展時(shí)炫彩,就會(huì)拋出OutOfMemoryError異常。
方法區(qū)
方法區(qū)也是各個(gè)線程共享的內(nèi)存區(qū)域絮短,用于存儲(chǔ)虛擬機(jī)加載的類信息江兢、常量、靜態(tài)變量丁频、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)杉允。
官方文檔中只規(guī)定了方法區(qū)這個(gè)概念,但沒有規(guī)定具體的實(shí)現(xiàn)方式席里。在JDK1.7以前叔磷,HotSpot虛擬機(jī)主要使用永久代的方式來實(shí)現(xiàn)方法區(qū),JDK1.8開始改為元空間奖磁。
運(yùn)行時(shí)常量池
運(yùn)行時(shí)常量池是方法區(qū)的一部分改基。Class文件中除了有類的版本、字段咖为、方法秕狰、接口等描述信息外稠腊,還有一項(xiàng)就是常量池,用于存放編譯期生成的各種字面量和符號(hào)引用鸣哀,這部分內(nèi)容在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池存放架忌。
運(yùn)行時(shí)常量池相比于Class文件常量池,具備動(dòng)態(tài)性我衬。Java并不要求常量一定只有編譯期才能產(chǎn)生叹放,運(yùn)行期間也可能有新的常量放入池中。例如String類的intern()方法挠羔。
關(guān)于String類的intern方法许昨,推薦一篇文章:https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html