此處的內(nèi)容是根據(jù)Java虛擬機(jī)規(guī)范(Java SE 7)相關(guān)內(nèi)容以及深入理解Java虛擬機(jī)等做的總結(jié)。可能有不對(duì)的地方贮泞。了解這些區(qū)域,可以從總體上看下虛擬機(jī)內(nèi)部是怎么構(gòu)造的幔烛,網(wǎng)上也有相關(guān)的圖片介紹啃擦,可以適當(dāng)?shù)挠浵聢D片內(nèi)容,這樣可以有一個(gè)立體的感受饿悬,更容易記憶令蛉。
Java虛擬機(jī)定義了程序運(yùn)行期間使用到的運(yùn)行時(shí)數(shù)據(jù)區(qū)域,其中一些與虛擬機(jī)生命周期相同狡恬,另外一些與線程的生命周期相同珠叔。JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)域分為:
- 程序計(jì)數(shù)器(Program Counter)
- Java虛擬機(jī)棧(Java Virtual Machine Stack)
- 堆(Heap)
- 方法區(qū)(Method Area)
- 本地方法棧(Native Method Stack)
程序計(jì)數(shù)器(Program Counter)
程序計(jì)數(shù)器是線程私有的,每條線程都有自己的程序計(jì)數(shù)器弟劲。
Java虛擬機(jī)是支持多線程的祷安,多線程是通過線程的輪流切換來實(shí)現(xiàn)的,也就是說每次切換都需要在上次停頓的地方重新開始運(yùn)行兔乞,這時(shí)候就需要程序計(jì)數(shù)器來保存當(dāng)前線程正在執(zhí)行的字節(jié)碼指令的地址汇鞭,切換到該線程的時(shí)候撇眯,就能知道該執(zhí)行哪一個(gè)字節(jié)碼指令了。
如果一個(gè)線程正在執(zhí)行的方法是Java方法虱咧,程序計(jì)數(shù)器保存的是Java虛擬機(jī)正在執(zhí)行的字節(jié)碼指令的地址熊榛;如果正在執(zhí)行的方法是native的,程序計(jì)數(shù)器的值為undefined腕巡。
Java虛擬機(jī)棧(Java Virtual Machine Stack)
Java虛擬機(jī)棧也是線程私有的玄坦,與線程同時(shí)創(chuàng)建,用于存儲(chǔ)棧幀(Fremas)绘沉,棧幀用來存儲(chǔ)局部變量煎楣,操作數(shù)棧、指向當(dāng)前方法所屬類的運(yùn)行時(shí)常量池车伞、處理動(dòng)態(tài)鏈接择懂、方法返回值和異常分派。方法從調(diào)用到執(zhí)行完成的過程就對(duì)應(yīng)著一個(gè)棧幀從入棧到出棧的過程另玖。
Java虛擬機(jī)椑铮可以被實(shí)現(xiàn)為固定大小的,此時(shí)每一條線程的Java虛擬機(jī)棧在線程創(chuàng)建的時(shí)候容量就已經(jīng)確定谦去;還可以被實(shí)現(xiàn)為根據(jù)計(jì)算動(dòng)態(tài)擴(kuò)展和收縮的慷丽。
Java虛擬機(jī)棧可能會(huì)發(fā)生異常:
- 如果線程請(qǐng)求的棧容量超過Java虛擬機(jī)棧允許的最大容量鳄哭,會(huì)拋出StackOverflowError異常要糊。
- 如果虛擬機(jī)棧可動(dòng)態(tài)擴(kuò)展妆丘,申請(qǐng)不到足夠的內(nèi)存去完成擴(kuò)展锄俄,或者建立新線程時(shí)沒有足夠的內(nèi)存去創(chuàng)建虛擬機(jī)棧,會(huì)拋出OutOfMemoryError異常勺拣。
棧幀
棧幀隨著方法的調(diào)用而創(chuàng)建奶赠,隨著方法的結(jié)束(正常或者異常結(jié)束)而銷毀宣脉,是用來存儲(chǔ)數(shù)據(jù)和部分過程結(jié)果的數(shù)據(jù)結(jié)構(gòu)车柠,同時(shí)也被用來處理動(dòng)態(tài)鏈接(Dynamic Linking)剔氏、方法返回值和異常分派(Dispatch Exception)塑猖。
棧幀存在于Java虛擬機(jī)棧中,棧幀中包含局部變量表谈跛、操作數(shù)棧和指向當(dāng)前方法所屬類的運(yùn)行時(shí)常量池的引用羊苟。
局部變量表和操作數(shù)棧的容量在編譯期確定,通過方法的Code屬性保存并提供給棧幀使用感憾。棧幀的容量大小僅僅取決于Java虛擬機(jī)的實(shí)現(xiàn)和方法調(diào)用時(shí)可分配的內(nèi)存蜡励。
局部變量表
局部變量表存在于棧幀中,長度在編譯期決定,存儲(chǔ)在類和接口的二進(jìn)制表示中凉倚,也就是存儲(chǔ)在方法的Code屬性中并提供給棧幀使用兼都。
局部變量表可以保存類型為boolean、byte稽寒、char扮碧、short、int杏糙、float慎王、reference、returnAddress宏侍,而long和double類型需要兩個(gè)局部變量表來存儲(chǔ)赖淤。
局部變量表還用來完成方法調(diào)用時(shí)參數(shù)的傳遞,一個(gè)方法被調(diào)用谅河,它的參數(shù)會(huì)傳遞至0開始的連續(xù)局部變量表位置上咱旱。對(duì)于實(shí)例方法來說,局部變量表第0個(gè)位置是用來存儲(chǔ)實(shí)例方法所在對(duì)象的引用绷耍,也就是我們通常說的this莽龟。
操作數(shù)棧
操作數(shù)棧存在于棧幀中,是一個(gè)LIFO的棧锨天,長度由編譯期確定毯盈,也是存儲(chǔ)在方法的Code屬性中提供給棧幀使用。
操作數(shù)棧會(huì)有一個(gè)確定的棧深度病袄,一個(gè)long或者double類型的數(shù)據(jù)會(huì)占用兩個(gè)單位的棧深度搂赋,其他數(shù)據(jù)類型則會(huì)占用一個(gè)單位深度。
動(dòng)態(tài)鏈接
棧幀內(nèi)部包含一個(gè)指向運(yùn)行時(shí)常量池的引用(運(yùn)行時(shí)常量池的解釋在下面益缠,可以先看一下運(yùn)行時(shí)常量池)脑奠,這個(gè)引用用來支持當(dāng)前方法的代碼實(shí)現(xiàn)動(dòng)態(tài)鏈接。
Class文件中幅慌,一個(gè)方法調(diào)用其他方法或者訪問其成員變量是通過符號(hào)引用來表示的宋欺,動(dòng)態(tài)鏈接作用就是將符號(hào)引用轉(zhuǎn)換為實(shí)際的直接引用。
堆(Heap)
堆是各個(gè)線程共享的運(yùn)行時(shí)內(nèi)存區(qū)域胰伍,也是所有的類實(shí)例和數(shù)組對(duì)象分配內(nèi)存的區(qū)域齿诞。堆在虛擬機(jī)啟動(dòng)的時(shí)候被創(chuàng)建,存儲(chǔ)了被垃圾收集器所管理的各種對(duì)象骂租。
堆的容量可以是固定大小的祷杈,也可以是動(dòng)態(tài)擴(kuò)展和自動(dòng)收縮的。Java堆的內(nèi)存不需要保證是連續(xù)的渗饮。
Java堆可能發(fā)生異常情況:
- 實(shí)際所需的堆超過了最大容量但汞,拋出OutOfMemoryError異常宿刮。
方法區(qū)(Method Area)
方法區(qū)也是被各個(gè)線程所共享的運(yùn)行時(shí)內(nèi)存區(qū)域。用于存儲(chǔ)類的結(jié)構(gòu)信息私蕾,例如運(yùn)行時(shí)常量池僵缺、字段、方法數(shù)據(jù)踩叭、構(gòu)造函數(shù)谤饭、普通方法的字節(jié)碼內(nèi)容、還包括一些在類懊纳、實(shí)例揉抵、接口初始化時(shí)用到的特殊方法。
方法區(qū)在虛擬機(jī)啟動(dòng)的時(shí)候被創(chuàng)建嗤疯,是堆的邏輯組成部分冤今。方法區(qū)的容量可以是固定大小的,也可以是動(dòng)態(tài)擴(kuò)展和自動(dòng)收縮的茂缚。內(nèi)存空間不需要保證是連續(xù)的戏罢。
方法區(qū)可能發(fā)生異常的情況:
- 方法區(qū)的內(nèi)存不能滿足內(nèi)存分配時(shí),會(huì)拋出OutOfMemoryError異常脚囊。
運(yùn)行時(shí)常量池
運(yùn)行時(shí)常量池分配在方法區(qū)中龟糕,類和接口被加載到虛擬機(jī)之后,運(yùn)行時(shí)常量池就被創(chuàng)建了悔耘。
運(yùn)行時(shí)常量池是類或接口的常量池的運(yùn)行時(shí)表示形式讲岁,包括從編譯期可知的數(shù)值字面量和運(yùn)行期解析后才能獲得的方法或字段引用。
可能會(huì)發(fā)生異常的情況:
- 構(gòu)造運(yùn)行時(shí)常量池所需的內(nèi)存空間超過了方法區(qū)能提供的最大值衬以,會(huì)拋出OutOfMemoryError異常缓艳。
本地方法棧(Native Method Stack)
用來支持native方法。跟虛擬機(jī)棧功能類似看峻。本地方法棧被實(shí)現(xiàn)成固定大小或者是動(dòng)態(tài)擴(kuò)展和收縮的阶淘。
可能會(huì)發(fā)生的異常情況:
- 如果線程請(qǐng)求的棧容量超過本地方法棧允許的最大容量,會(huì)拋出StackOverflowError異常互妓。
- 如果本地方法椣希可動(dòng)態(tài)擴(kuò)展,申請(qǐng)不到足夠的內(nèi)存去完成擴(kuò)展冯勉,或者建立新線程時(shí)沒有足夠的內(nèi)存去創(chuàng)建對(duì)應(yīng)的本地方法棧澈蚌,會(huì)拋出OutOfMemoryError異常。
簡要總結(jié)
程序計(jì)數(shù)器為線程私有珠闰,用來指示程序運(yùn)行時(shí)的位置惜浅。
Java虛擬機(jī)棧是線程私有的,用來存儲(chǔ)局部變量表等伏嗜,出棧入棧對(duì)應(yīng)著方法的結(jié)束開始坛悉。
堆是線程共享的區(qū)域,虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建承绸,創(chuàng)建的實(shí)例對(duì)象和數(shù)組都分配在堆上裸影。
方法區(qū)是線程共享的區(qū)域,虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建军熏,用來存儲(chǔ)類的信息轩猩,常量字段等等。
本地方法棧用來執(zhí)行本地方法的荡澎。