Java在執(zhí)行Java程序的過程中會(huì)把它所管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū)域。這些區(qū)域都有各自的用途、創(chuàng)建和銷毀的時(shí)間,有一些是隨虛擬機(jī)的啟動(dòng)而創(chuàng)建,隨虛擬機(jī)的退出而銷毀,有些則是與線程一一對應(yīng),隨線程的開始和結(jié)束而創(chuàng)建和銷毀。
Java虛擬機(jī)所管理的內(nèi)存將會(huì)包括以下幾個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū)域
它是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)先線程所執(zhí)行的字節(jié)碼的信號指示器。
每一條JVM線程都有自己的PC寄存器,各條線程之間互不影響,獨(dú)立存儲(chǔ),這類內(nèi)存區(qū)域被稱為“線程私有”內(nèi)存
在任意時(shí)刻,一條JVM線程只會(huì)執(zhí)行一個(gè)方法的代碼。該方法稱為該線程的當(dāng)前方法(Current Method)
如果該方法是java方法,那PC寄存器保存JVM正在執(zhí)行的字節(jié)碼指令的地址
如果該方法是native,那PC寄存器的值是undefined。
此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。
與PC寄存器一樣,Java虛擬機(jī)棧也是線程私有的。每一個(gè)JVM線程都有自己的java虛擬機(jī)棧,這個(gè)棧與線程同時(shí)創(chuàng)建,它的生命周期與線程相同。
虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法被執(zhí)行的時(shí)候都會(huì)同時(shí)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。每一個(gè)方法被調(diào)用直至執(zhí)行完成的過程就對應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程。
JVM stack 可以被實(shí)現(xiàn)成固定大小,也可以根據(jù)計(jì)算動(dòng)態(tài)擴(kuò)展。
如果采用固定大小的JVM stack設(shè)計(jì),那么每一條線程的JVM Stack容量應(yīng)該在線程創(chuàng)建時(shí)獨(dú)立地選定。JVM實(shí)現(xiàn)應(yīng)該提供調(diào)節(jié)JVM Stack初始容量的手段;如果采用動(dòng)態(tài)擴(kuò)展和收縮的JVM Stack方式,應(yīng)該提供調(diào)節(jié)最大、最小容量的手段。
如果線程請求的棧深度大于虛擬機(jī)所允許的深度將拋出StackOverflowError;
如果JVM Stack可以動(dòng)態(tài)擴(kuò)展,但是在嘗試擴(kuò)展時(shí)無法申請到足夠的內(nèi)存時(shí)拋出OutOfMemoryError。
本地方法棧與虛擬機(jī)棧作用相似,后者為虛擬機(jī)執(zhí)行Java方法服務(wù),而前者為虛擬機(jī)用到的Native方法服務(wù)。
虛擬機(jī)規(guī)范對于本地方法棧中方法使用的語言,使用方式和數(shù)據(jù)結(jié)構(gòu)沒有強(qiáng)制規(guī)定,甚至有的虛擬機(jī)(比如HotSpot)直接把二者合二為一。
這玩意兒拋出的異常跟上面的虛擬機(jī)棧一樣。
虛擬機(jī)管理的內(nèi)存中最大的一塊,同時(shí)也是被所有線程所共享的,它在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建,這貨存在的意義就是存放對象實(shí)例,幾乎所有的對象實(shí)例以及數(shù)組都要在這里分配內(nèi)存。這里面的對象被自動(dòng)管理,也就是俗稱的GC(Garbage Collector)所管理。用就是了,有GC扛著呢,不用操心銷毀回收的事兒。
Java堆的容量可以是固定大小,也可以隨著需求動(dòng)態(tài)擴(kuò)展(-Xms和-Xmx),并在不需要過多空間時(shí)自動(dòng)收縮。
Java堆所使用的內(nèi)存不需要保證是物理連續(xù)的,只要邏輯上是連續(xù)的即可。
JVM實(shí)現(xiàn)應(yīng)當(dāng)提供給程序員調(diào)節(jié)Java 堆初始容量的手段,對于可動(dòng)態(tài)擴(kuò)展和收縮的堆來說,則應(yīng)當(dāng)提供調(diào)節(jié)其最大和最小容量的手段。
如果堆中沒有內(nèi)存完成實(shí)例分配并且堆也無法擴(kuò)展,就會(huì)拋OutOfMemoryError。
跟堆一樣是被各個(gè)線程共享的內(nèi)存區(qū)域,用于存儲(chǔ)以被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。雖然這個(gè)區(qū)域被虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分,但是它的別名叫非堆,用來與堆做一下區(qū)別。
方法區(qū)在虛擬機(jī)啟動(dòng)的時(shí)候創(chuàng)建。
方法區(qū)的容量可以是固定大小的,也可以隨著程序執(zhí)行的需求動(dòng)態(tài)擴(kuò)展,并在不需要過多空間時(shí)自動(dòng)收縮。
方法區(qū)在實(shí)際內(nèi)存空間中可以是不連續(xù)的。
Java虛擬機(jī)實(shí)現(xiàn)應(yīng)當(dāng)提供給程序員或者最終用戶調(diào)節(jié)方法區(qū)初始容量的手段,對于可以動(dòng)態(tài)擴(kuò)展和收縮方法區(qū)來說,則應(yīng)當(dāng)提供調(diào)節(jié)其最大、最小容量的手段。
當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時(shí)就會(huì)拋OutOfMemoryError。
這里有一個(gè)小例子,來說明堆,棧和方法區(qū)之間的關(guān)系的
public
class
Test2 {
public
static
void
main(String[] args) {
public
Test2 t2 =
new
Test2();
//JVM將Test2類信息加載到方法區(qū),new Test2()實(shí)例保存在堆區(qū),Test2引用保存在棧區(qū)
}
}
![]() | ![]() .. 定價(jià):¥45 優(yōu)惠價(jià):¥42 更多書籍 |
![]() | ![]() .. 定價(jià):¥225 優(yōu)惠價(jià):¥213 更多書籍 |