Java 虛擬機(jī)棧詳解分析
Java虛擬機(jī)棧
1. 定義
- 棧:線(xiàn)程運(yùn)行時(shí)需要的內(nèi)存空間,一個(gè)棧存在多個(gè)棧幀。棧具有先入后出,后入先出的特點(diǎn)。
- 棧幀:每個(gè)方法運(yùn)行時(shí)需要的內(nèi)存(局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接和方法返回值等信息。),每次調(diào)用一個(gè)方法,便會(huì)將棧幀壓入棧中,方法執(zhí)行完畢將棧幀從棧頂壓出
- 活動(dòng)棧幀:指在棧頂?shù)臈日谡{(diào)用的方法,每個(gè)線(xiàn)程只能有一個(gè)活動(dòng)棧幀,對(duì)應(yīng)著該線(xiàn)程正在調(diào)用的那個(gè)方法
現(xiàn)在我們用代碼來(lái)演示一下Java虛擬機(jī)如何將棧幀壓入及壓出棧中
public class Main { public static void main(String[] args) { method1(); } private static void method1() { method2(1, 2); } private static int method2(int a, int b) { int c = a + b; return c; } }
當(dāng)我們運(yùn)行Main函數(shù)時(shí),jvm首先將棧幀Main壓入棧中,此視棧結(jié)構(gòu)如圖所示
Main函數(shù)體中調(diào)用了method1方法,此時(shí)便會(huì)將棧幀1壓入棧中
method1方法體中調(diào)用了method2方法,這時(shí)jvm會(huì)將棧幀2壓入棧結(jié)構(gòu)中,需要注意的是,前面我們提到了棧幀由 “局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接和方法返回值”等信息組成,method2方法中擁有a,b兩個(gè)參數(shù)以及局部變量c和方法返回值
當(dāng)method2方法執(zhí)行完后,會(huì)將棧幀2從棧頂彈出
method1方法執(zhí)行完畢后,將棧幀1彈出
依次順序直至線(xiàn)程被銷(xiāo)毀。
注意點(diǎn)
- 由于每個(gè)棧幀都會(huì)在方法調(diào)用完畢后被彈出,因此棧內(nèi)存不需要進(jìn)行垃圾回收
- 每個(gè)棧都是線(xiàn)程私有的,每個(gè)線(xiàn)程在創(chuàng)建的時(shí)候都會(huì)創(chuàng)建一個(gè)虛擬機(jī)棧,而由于物理內(nèi)存是固定的,棧內(nèi)存劃分得越大,可分配的線(xiàn)程數(shù)就越少
2. 棧的線(xiàn)程安全問(wèn)題
局部變量是線(xiàn)程安全的
現(xiàn)在我們定義如下方法:
public static void method() { int a = 0; a++; }
我們?cè)诜椒╩ethod中定義了一個(gè)局部變量a,并對(duì)其執(zhí)行a++操作,現(xiàn)在假設(shè)我們有兩個(gè)線(xiàn)程同時(shí)調(diào)用了這個(gè)方法(棧幀),Java虛擬機(jī)會(huì)將該棧幀壓入各自線(xiàn)程的棧內(nèi)存中,但由于局部變量表是線(xiàn)程私有的,所以?xún)蓚€(gè)線(xiàn)程在同時(shí)調(diào)用這個(gè)棧幀后,a的值仍然都為1,故局部變量是線(xiàn)程安全的
方法參數(shù)和方法返回值不是線(xiàn)程安全
由于方法的參數(shù)和返回值均可被外部方法所引用,故在某個(gè)線(xiàn)程下某個(gè)方法可以更改另外一個(gè)方法的參數(shù)和返回值,故方法參數(shù)和方法返回值不是線(xiàn)程安全的
3. 棧內(nèi)存溢出
由于程序執(zhí)行時(shí),虛擬機(jī)給每個(gè)棧分配的棧內(nèi)存空間是固定的,所以在一些情況下有可能出現(xiàn)棧內(nèi)存空間不足,導(dǎo)致溢出的情況,一般有兩種情況可能導(dǎo)致棧內(nèi)存溢出
- 棧幀過(guò)大(較少出現(xiàn))
- 棧幀過(guò)多(一般出現(xiàn)在遞歸時(shí)。沒(méi)有正確設(shè)置遞歸出口)
現(xiàn)在我們來(lái)解釋一下什么時(shí)候會(huì)出現(xiàn)棧幀過(guò)多導(dǎo)致棧內(nèi)存溢出,我們來(lái)假設(shè)某一個(gè)棧的內(nèi)存空間大小是1024kb,現(xiàn)在有四個(gè)棧幀,每個(gè)棧幀的大小均為300kb,而 300 * 4 = 1200kb, 而 1200 > 1024 ,很顯然,現(xiàn)在這四個(gè)棧幀合起來(lái)的大小已經(jīng)超過(guò)了這個(gè)棧的內(nèi)存空間大小,這個(gè)時(shí)候就會(huì)出現(xiàn)棧內(nèi)存溢出,也就是會(huì)報(bào)java.lang.StackOverflowError這個(gè)錯(cuò)誤。
到此這篇關(guān)于Java 虛擬機(jī)棧詳解分析的文章就介紹到這了,更多相關(guān)Java 虛擬機(jī)棧內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(36)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07IDEA 開(kāi)發(fā)配置SparkSQL及簡(jiǎn)單使用案例代碼
這篇文章主要介紹了IDEA 開(kāi)發(fā)配置SparkSQL及簡(jiǎn)單使用案例代碼,本文通過(guò)代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08JDK21新特性Record?Patterns記錄模式詳解(最新推薦)
這篇文章主要介紹了JDK21新特性Record?Patterns記錄模式詳解,本JEP建立在Pattern?Matching?for?instanceof(JEP?394)的基礎(chǔ)上,該功能已在JDK?16中發(fā)布,它與Pattern?Matching?for?switch(JEP?441)共同演進(jìn),需要的朋友可以參考下2023-09-09Java的關(guān)鍵字與標(biāo)識(shí)符小結(jié)
這篇文章主要介紹了Java的關(guān)鍵字與標(biāo)識(shí)符,總結(jié)整理了Java各種常見(jiàn)的關(guān)鍵字與標(biāo)識(shí)符功能、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04intellij idea創(chuàng)建第一個(gè)動(dòng)態(tài)web項(xiàng)目的步驟方法
這篇文章主要介紹了intellij idea創(chuàng)建第一個(gè)動(dòng)態(tài)web項(xiàng)目的步驟方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10Java ArrayList擴(kuò)容機(jī)制原理深入分析
在Java中,ArrayList是最常用的集合之一。它是一種容器,它的內(nèi)部定義了一個(gè)Object類(lèi)型的數(shù)組elementData,因此可用于存儲(chǔ)任意類(lèi)型的數(shù)據(jù)。我們知道,數(shù)組是長(zhǎng)度恒定的。而ArrayList相當(dāng)于是一個(gè)長(zhǎng)度可變的動(dòng)態(tài)數(shù)組,一起來(lái)看看的它的擴(kuò)容機(jī)制2023-02-02Java實(shí)現(xiàn)簡(jiǎn)單的聊天室功能
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06