JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)域
Java虛擬機(jī)定義了在程序執(zhí)行期間使用的多個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū)域愕掏。其中一些數(shù)據(jù)區(qū)域是在Java虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建的,只有當(dāng)Java虛擬機(jī)退出時(shí)才會(huì)銷毀分苇。其他數(shù)據(jù)區(qū)域是每個(gè)線程私有的。線程私有的數(shù)據(jù)區(qū)域在線程創(chuàng)建時(shí)創(chuàng)建横侦,在線程退出時(shí)銷毀奕塑。
程序計(jì)數(shù)器
Java虛擬機(jī)可以同時(shí)支持多個(gè)線程執(zhí)行悍引。每個(gè)線程擁有自己的程序計(jì)數(shù)器。在任何時(shí)刻机断,每個(gè)Java虛擬機(jī)線程都在執(zhí)行一個(gè)方法的代碼楷拳,即該線程的當(dāng)前方法。如果該方法不是本地方法(native method
),程序計(jì)數(shù)器包含當(dāng)前正在執(zhí)行的Java虛擬機(jī)指令地址。如果線程正在執(zhí)行的是本地方法(native method
)酿傍,程序計(jì)數(shù)器的值為空砾莱。Java虛擬機(jī)的程序計(jì)數(shù)器大小足夠在特定平臺(tái)上保存一個(gè)返回地址或本地指針。
Java虛擬機(jī)棧
每個(gè)Java虛擬機(jī)線程都有一個(gè)私有的Java虛擬機(jī)棧饲漾,與線程同時(shí)創(chuàng)建。Java虛擬機(jī)棧里面存儲(chǔ)棧幀。方法從調(diào)用直至執(zhí)行完成的過程坤按,對(duì)應(yīng)著棧幀在虛擬機(jī)棧中從入棧到出棧的過程。因?yàn)槌藥娜霔:统鰲B琂ava虛擬機(jī)棧從來不會(huì)被直接操作臭脓,所以幀可以是堆分配的。Java虛擬機(jī)棧的內(nèi)存不需要是連續(xù)的腹忽。
每個(gè)方法在執(zhí)行時(shí)會(huì)創(chuàng)建一個(gè)棧幀来累,用于存儲(chǔ)局部變量表、操作數(shù)棧窘奏、動(dòng)態(tài)鏈接嘹锁、方法出口等信息。局部變量表中存放了編譯期可知的基本數(shù)據(jù)類型和對(duì)象引用着裹。局部變量表所需的內(nèi)存空間大小在編譯期確定兼耀,運(yùn)行時(shí)不會(huì)改變。
Java虛擬機(jī)規(guī)范允許Java虛擬機(jī)棧是固定大小的求冷,也可以根據(jù)計(jì)算需要?jiǎng)討B(tài)擴(kuò)展和收縮瘤运。如果Java虛擬機(jī)棧是固定大小的,那么創(chuàng)建時(shí)應(yīng)該可以獨(dú)立地選擇每個(gè)Java虛擬機(jī)棧的大小匠题。
下列異常情況與Java虛擬機(jī)棧有關(guān):
- 如果一個(gè)線程計(jì)算需要的Java虛擬機(jī)棧大小超過了允許的大小拯坟,Java虛擬機(jī)將拋出
StackOverflowError
。 - 如果Java虛擬機(jī)椌律剑可以動(dòng)態(tài)擴(kuò)展郁季,但是嘗試擴(kuò)展時(shí)內(nèi)存不足冷溃,或者新建線程的時(shí)候沒有足夠的內(nèi)存來創(chuàng)建Java虛擬機(jī)棧,Java虛擬機(jī)將拋出
OutOfMemoryError
梦裂。
堆
Java虛擬機(jī)有一個(gè)堆似枕,它在所有Java虛擬機(jī)線程之間共享。堆是運(yùn)行時(shí)數(shù)據(jù)區(qū)域年柠,所有類實(shí)例和數(shù)組的內(nèi)存都從堆中分配凿歼。
堆在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。對(duì)象的堆存儲(chǔ)由一個(gè)自動(dòng)存儲(chǔ)管理系統(tǒng)(稱為垃圾回收器)回收冗恨;對(duì)象從來不會(huì)顯式地釋放答憔。Java虛擬機(jī)沒有特定類型的自動(dòng)存儲(chǔ)管理系統(tǒng),可以根據(jù)實(shí)現(xiàn)者的系統(tǒng)需求選擇存儲(chǔ)管理技術(shù)掀抹。堆可以是固定大小的虐拓,也可以根據(jù)計(jì)算需要擴(kuò)展,如果不需要更大的堆傲武,則可以收縮蓉驹。堆的內(nèi)存不需要是連續(xù)的。
從內(nèi)存回收的角度看揪利,由于垃圾回收器基本都采用分代回收算法态兴,所以Java堆還可以細(xì)分為:新生代和老年代;再細(xì)致一點(diǎn)的有Eden空間土童、From Survivor空間诗茎、To Survivor空間等。從內(nèi)存分配的角度看献汗,線程共享的Java堆中可能劃分出多個(gè)線程私有的分配緩沖區(qū)(Thread Local Allocation Buffer敢订,TLAB)。
下列異常情況與堆有關(guān):
- 如果計(jì)算時(shí)需要比自動(dòng)存儲(chǔ)管理系統(tǒng)可以提供的更多的堆內(nèi)存罢吃,Java虛擬機(jī)將拋出
OutOfMemoryError
楚午。
NIO中可以使用Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個(gè)存儲(chǔ)在Java堆中
DirectByteBuffer
對(duì)象作為這塊內(nèi)存的引用進(jìn)行操作尿招。這樣可以在一些場景中提高性能矾柜,因?yàn)楸苊饬嗽贘ava堆和Native堆中來回復(fù)制數(shù)據(jù)。雖然直接內(nèi)存的分配不會(huì)受到Java堆大小的限制就谜,但是肯定會(huì)受到本機(jī)總內(nèi)存的限制怪蔑。因此也可能拋出OutOfMemoryError
。
方法區(qū)
Java虛擬機(jī)有一個(gè)方法區(qū)丧荐,該方法區(qū)在所有Java虛擬機(jī)線程之間共享缆瓣。方法區(qū)類似于傳統(tǒng)語言編譯代碼的存儲(chǔ)區(qū)域,或類似于操作系統(tǒng)進(jìn)程中的“文本”段虹统。它存儲(chǔ)每個(gè)類的結(jié)構(gòu)弓坞,比如運(yùn)行時(shí)常量池隧甚、字段和方法數(shù)據(jù),以及方法和構(gòu)造函數(shù)的代碼渡冻,包括在類和接口初始化以及實(shí)例初始化中使用的特殊方法戚扳。
方法區(qū)在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。盡管方法區(qū)在邏輯上是堆的一部分族吻,但是簡單的實(shí)現(xiàn)可能選擇不進(jìn)行垃圾收集或壓縮帽借。Java虛擬機(jī)規(guī)范不強(qiáng)制要求方法區(qū)的位置或用于管理編譯代碼的策略。方法區(qū)可以是固定大小的呼奢,也可以根據(jù)計(jì)算的需要擴(kuò)展宜雀,如果不需要更大的方法區(qū)切平,則可以收縮握础。方法區(qū)的內(nèi)存不需要是連續(xù)的。
下列異常情況與方法區(qū)有關(guān):
- 如果方法區(qū)中的內(nèi)存不能滿足一個(gè)分配請(qǐng)求悴品,Java虛擬機(jī)將拋出
OutOfMemoryError
禀综。
運(yùn)行時(shí)常量池
一個(gè)運(yùn)行時(shí)常量池是一個(gè)類或接口在class
文件中的constant_pool
表的運(yùn)行時(shí)表現(xiàn)。它包含幾種常量苔严,從編譯時(shí)已知的數(shù)值常量到必須在運(yùn)行時(shí)解析的方法和字段引用定枷。運(yùn)行時(shí)常量池的功能類似于傳統(tǒng)編程語言的符號(hào)表,盡管它包含的數(shù)據(jù)范圍比典型的符號(hào)表更廣届氢。
每個(gè)運(yùn)行時(shí)常量池都從Java虛擬機(jī)的方法區(qū)分配欠窒。類或接口的運(yùn)行時(shí)常量池是在Java虛擬機(jī)創(chuàng)建類或接口時(shí)構(gòu)造的。
以下異常情況與類或接口的運(yùn)行時(shí)常量池的構(gòu)造有關(guān):
- 當(dāng)創(chuàng)建一個(gè)類或接口時(shí)退子,如果構(gòu)造運(yùn)行時(shí)常量池需要的內(nèi)存超過了Java虛擬機(jī)方法區(qū)的可用內(nèi)存岖妄,Java虛擬機(jī)將拋出
OutOfMemoryError
。
本地方法棧
Java虛擬機(jī)實(shí)現(xiàn)可以使用傳統(tǒng)棧寂祥,俗稱"C"棧荐虐,來支持本地方法。Java虛擬機(jī)指令集的解釋器實(shí)現(xiàn)也可以使用本地方法棧丸凭。不能加載本地方法且本身不依賴于傳統(tǒng)棧的Java虛擬機(jī)實(shí)現(xiàn)可以不提供本地方法棧福扬。如果提供了本地方法棧,通常在創(chuàng)建線程時(shí)為每個(gè)線程分配本地方法棧惜犀。
Java虛擬機(jī)規(guī)范允許本地方法棧是固定大小的铛碑,也可以根據(jù)計(jì)算需要?jiǎng)討B(tài)擴(kuò)展或收縮。如果本地方法棧是固定大小的虽界,每個(gè)本地方法棧的大小應(yīng)該在創(chuàng)建時(shí)可以獨(dú)立選擇汽烦。
下列異常情況與本地方法棧有關(guān):
- 如果線程計(jì)算需要的本地方法棧大小超過了允許的大小,Java虛擬機(jī)將拋出
StackOverflowError
浓恳。 - 如果本地方法椛卜欤可以動(dòng)態(tài)擴(kuò)展碗暗,但是嘗試擴(kuò)展本地方法棧時(shí)內(nèi)存不足,或者新建線程的時(shí)候沒有足夠的內(nèi)存來創(chuàng)建本地方法棧梢夯,Java虛擬機(jī)將拋出
OutOfMemoryError
言疗。