在介紹堆棧之前簡單說下JVM的內(nèi)存結(jié)構(gòu)捐腿,一共分為虛擬機(jī)棧、堆柿顶、方法區(qū)茄袖、程序計數(shù)器、本地方法棧五個部分:
棧:
?線程私有嘁锯,生命周期和線程生命周期相同宪祥;
?棧由一些列幀組成聂薪;
?每個方法在創(chuàng)建的執(zhí)行的時候都會創(chuàng)建一個棧幀,用來存儲局部變量蝗羊、操作數(shù)棧藏澳、動態(tài)鏈接、方法返回地址等耀找;
?每個方法從調(diào)用到執(zhí)行完畢對應(yīng)一個棧幀在虛擬機(jī)棧中的入棧和出棧
堆:線程共享翔悠,在虛擬機(jī)啟動的時候創(chuàng)建,用來存放對象實例野芒,是GC管理的主要區(qū)域
方法區(qū):線程共享蓄愁,用于存儲已被虛擬機(jī)加載的類信息、類型的常量池狞悲、靜態(tài)變量撮抓、字段和方法信息等
程序計數(shù)器:線程私有,是當(dāng)前線程所執(zhí)行的字節(jié)碼行號指示器效诅,每個線程都有一個獨立的程序計數(shù)器胀滚,字節(jié)碼解釋器工作時通過改變它的值來選取下一條需要執(zhí)行的字節(jié)碼指令。
本地方法棧:線程私有乱投,主要為虛擬機(jī)用到的native方法服務(wù)咽笼,與虛擬機(jī)棧類似
簡單描述下堆棧的各自特點:
比較通俗的講就是棧是用來執(zhí)行程序的,堆主要是用來存放對象的戚炫。棧是一種具有先進(jìn)后出性質(zhì)的數(shù)據(jù)結(jié)構(gòu)剑刑,也就是說后存放的先取出。堆是一種經(jīng)過排序的樹形數(shù)據(jù)結(jié)構(gòu)双肤,每個節(jié)點都有一個值施掏。通常所說的堆的數(shù)據(jù)結(jié)構(gòu)也就是二叉堆。
SOF全稱StackOverFlowError是指棧溢出茅糜,由上面介紹可知棧是義幀為單位保存線程運(yùn)行狀態(tài)的七芭,當(dāng)線程調(diào)用一個方法時JVM會壓入一個新的棧幀到這個線程的棧空間中蔑赘,當(dāng)方法執(zhí)行結(jié)束時棧幀會出棧狸驳,但是在方法執(zhí)行過程中,這個棧幀會一直存在的缩赛。所以如果方法的嵌套調(diào)用層次太多耙箍,隨著棧幀的增加導(dǎo)致總和大于JVM設(shè)置的-Xss值就會拋出StackOverFlowError。
OOM全程OutOfMemoryError 可能分為幾種情況:
堆內(nèi)存溢出:當(dāng)需要為創(chuàng)建的對象實例化分配堆內(nèi)存空間時酥馍,如果此時堆的占用已達(dá)到了設(shè)置的最大值(通過 -Xmx)辩昆,就會拋出OutOfMemoryError
方法區(qū)內(nèi)存溢出:在類加載器加載class文件到內(nèi)存時,JVM會提取類的信息(包括:類名旨袒、訪問修飾符汁针、常量池术辐、字段描述、方法描述等)到方法區(qū)扇丛,而此時如果需要存儲這些類信息但是方法區(qū)的內(nèi)存占用又已達(dá)到最大值术吗,就會拋出OutOfMemoryError
下面給出兩個例子可以看下:
//堆溢出例子OOM
public void heapException(){
for(;;) {
ArrayList list = new ArrayList (2000);
}
}
//棧溢出例子SOF
int count = 1;
public void stackException(){
count++;
this.stackException();
}