【JVM之運(yùn)行時(shí)數(shù)據(jù)區(qū)1】程序計(jì)數(shù)器妓布、虛擬機(jī)棧姻蚓、本地方法棧

一、運(yùn)行時(shí)數(shù)據(jù)區(qū)

我們?cè)诰帉慗ava程序時(shí)匣沼,使用JVM的流程主要如下所示:


在這里插入圖片描述

虛擬機(jī)在執(zhí)行Java程序時(shí)狰挡,會(huì)把它所管理的內(nèi)存劃分為不同的數(shù)據(jù)區(qū)域,即運(yùn)行時(shí)數(shù)據(jù)區(qū)释涛。有些數(shù)據(jù)區(qū)域是線程共享的加叁,即這些區(qū)域會(huì)隨著虛擬機(jī)的啟動(dòng)而創(chuàng)建,隨著虛擬機(jī)的關(guān)閉而銷毀唇撬。而另一些區(qū)域則是與線程對(duì)應(yīng)它匕,屬于線程私有的。這些區(qū)域會(huì)隨著線程開始而創(chuàng)建窖认,隨著線程的結(jié)束而銷毀豫柬。

具體的劃分如下:
多個(gè)線程共享的:堆、方法區(qū)
每個(gè)線程私有的:程序計(jì)數(shù)器扑浸、Java虛擬機(jī)棧烧给、本地方法棧

圖示如下:


在這里插入圖片描述

關(guān)于Java線程:在虛擬機(jī)中,每個(gè)線程都與操作系統(tǒng)的本地線程是直接對(duì)應(yīng)的喝噪。當(dāng)Java的線程準(zhǔn)備執(zhí)行時(shí)础嫡,操作系統(tǒng)的線程也同時(shí)創(chuàng)建了。

二酝惧、程序計(jì)數(shù)器

程序計(jì)數(shù)器是一塊很小的內(nèi)存空間驰吓,可以認(rèn)為是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器,負(fù)責(zé)保存當(dāng)前線程正在執(zhí)行的字節(jié)碼地址系奉。

1檬贰、程序計(jì)數(shù)器的作用

① Java是支持多線程的,這也意味著CPU會(huì)不停地切換線程缺亮。虛擬機(jī)的多線程是通過CPU時(shí)間片輪轉(zhuǎn)法來實(shí)現(xiàn)的翁涤,即每個(gè)線程占用CPU的時(shí)間是同等的桥言,時(shí)間一到就會(huì)切換線程,如此重復(fù)葵礼,直到線程終止号阿。這也意味著某個(gè)線程會(huì)因?yàn)闀r(shí)間片到而被掛起,所以當(dāng)該線程再次獲得時(shí)間片時(shí)鸳粉,需要知道從哪個(gè)地方繼續(xù)執(zhí)行扔涧,此時(shí)虛擬機(jī)只需要讀取程序計(jì)數(shù)器的值就可以知道要執(zhí)行的字節(jié)碼指令的位置了。這也是程序計(jì)數(shù)器是線程私有的原因届谈,因?yàn)槿舫绦蛴?jì)數(shù)器不是線程私有的話枯夜,當(dāng)CPU切換線程時(shí),會(huì)按照上一個(gè)線程的字節(jié)碼指令的位置來執(zhí)行當(dāng)前線程的字節(jié)碼指令艰山,這顯然是不正確的
② JVM的字節(jié)碼解釋器也需要通過改變程序計(jì)數(shù)器的值來明確下一條字節(jié)碼指令湖雹。

2、程序計(jì)數(shù)器的特點(diǎn)

① 程序計(jì)數(shù)器是數(shù)據(jù)區(qū)中唯一沒有規(guī)定OutOfMemoryError(內(nèi)存溢出異常)的區(qū)域曙搬。
②如果一個(gè)線程執(zhí)行的是Native本地方法摔吏,那么程序計(jì)數(shù)器的值為undefined。因?yàn)镴VM在執(zhí)行Native本地方法時(shí)纵装,是通過JNI調(diào)用本地其他語言來實(shí)現(xiàn)的征讲,而不是字節(jié)碼。

三橡娄、虛擬機(jī)棧

棧是運(yùn)行時(shí)的單位诗箍,堆是存儲(chǔ)的單位。
棧解決程序的運(yùn)行問題瀑踢,即方法如何執(zhí)行(或者如何處理數(shù)據(jù))。
堆解決的是數(shù)據(jù)存儲(chǔ)問題才避,即數(shù)據(jù)怎么放橱夭,在那放。

1桑逝、什么是虛擬機(jī)棧

虛擬機(jī)棧在每個(gè)線程開始時(shí)隨之創(chuàng)建棘劣,虛擬機(jī)棧與數(shù)據(jù)結(jié)構(gòu)中的棧一致,也是遵循先進(jìn)后出的原則楞遏。
虛擬機(jī)棧進(jìn)行出棧入棧操作的元素就是棧幀茬暇。每一個(gè)棧幀就對(duì)應(yīng)著一個(gè)方法,也可以將棧幀理解為一個(gè)方法的運(yùn)行空間寡喝。
一個(gè)棧幀入棧糙俗,意味著一個(gè)方法被調(diào)用,一個(gè)棧幀出棧预鬓,即一個(gè)方法執(zhí)行完畢巧骚。則棧幀的入棧順序就是方法的調(diào)用順序。

有代碼:

public class VMStackTest {

    private static int i = 1;

    public static void main(String[] args) {
        VMStackTest test = new VMStackTest();
        test.add();
        System.out.println(i);
    }

    public void add(){
        i++;
    }
}

代碼所示的虛擬機(jī)棧示例如下:

在這里插入圖片描述

同一時(shí)刻,在同一線程中劈彪,只有位于虛擬機(jī)棧頂部的棧幀才是有效運(yùn)行的竣蹦,即只有位于棧頂?shù)姆椒ㄊ钦趫?zhí)行的。執(zhí)行引擎運(yùn)行的所有字節(jié)碼指令都只針對(duì)當(dāng)前棧幀進(jìn)行操作沧奴。 如果在被調(diào)用的A方法中痘括,又調(diào)用了B方法,那么對(duì)應(yīng)的B方法的棧幀就會(huì)被創(chuàng)建滔吠,并被放在虛擬機(jī)棧的棧頂纲菌,成為新的正在執(zhí)行的方法。

對(duì)于虛擬機(jī)棧來說屠凶,不存在垃圾回收問題驰后。

Java方法有兩種方法返回方式,一種是正常的方法返回矗愧,使用return指令灶芝;另一種是拋出異常。不管是哪一種唉韭,都會(huì)導(dǎo)致棧幀被彈出夜涕。

2、棧幀的內(nèi)部結(jié)構(gòu)

每個(gè)棧幀都存儲(chǔ)著:
① 局部變量表
②操作數(shù)棧
③方法返回地址
④動(dòng)態(tài)鏈接
⑤一些附加信息

圖示如下:


在這里插入圖片描述

(1)属愤、局部變量表
局部變量表定義為一個(gè)數(shù)字?jǐn)?shù)組女器,用于存放方法參數(shù)和方法內(nèi)定義的局部變量
局部變量表建立在線程的虛擬機(jī)棧上住诸,是線程的私有數(shù)據(jù)驾胆,因此不存在數(shù)據(jù)安全問題。
虛擬機(jī)通過索引定位的方法查找相應(yīng)的局部變量贱呐,索引的范圍是從0~局部變量表最大容量丧诺。
局部變量表所需的容量大小是在編譯期定下的,方法運(yùn)行期間是不會(huì)該變局部變量表的大小的奄薇。

(2)驳阎、操作數(shù)棧
操作數(shù)棧也是一個(gè)棧的數(shù)據(jù)結(jié)構(gòu),操作數(shù)棧的最大深度也在編譯的時(shí)候定下馁蒂。
操作數(shù)棧的每一個(gè)元素可以是任意Java數(shù)據(jù)類型呵晚,在方法執(zhí)行過程中,操作數(shù)棧的深度都不會(huì)超過最大值沫屡。
操作數(shù)棧的作用是: 隨著方法執(zhí)行和字節(jié)碼指令的執(zhí)行饵隙,會(huì)從局部變量表或?qū)ο髮?shí)例的字段中復(fù)制常量或變量寫入到操作數(shù)棧,再隨著計(jì)算的進(jìn)行將棧中元素出棧到局部變量表或者返回給方法調(diào)用者沮脖,也就是出棧/入棧操作癞季。一個(gè)完整的方法執(zhí)行期間往往包含多個(gè)這樣出棧/入棧的過程劫瞳。

(3)、方法返回地址
一個(gè)方法結(jié)束绷柒,有正常執(zhí)行完畢退出和拋出異常(沒catch)志于,非正常退出兩種。
無論是那種方式废睦,在退出后都會(huì)返回到該方法被調(diào)用的位置伺绽。
正常退出時(shí),調(diào)用者的程序計(jì)數(shù)器的值會(huì)作為返回地址嗜湃,即調(diào)用該方法的指令的下一條指令的地址奈应。但是異常退出時(shí),返回地址要通過異常表來確定购披。
實(shí)際上杖挣,方法的退出就是當(dāng)前棧幀出棧的過程。故需要恢復(fù)上一層棧幀的局部變量表刚陡、操作數(shù)棧惩妇、將返回值壓入調(diào)用者棧幀的操作數(shù)棧,并設(shè)置其程序計(jì)數(shù)器的值等筐乳,讓調(diào)用者棧幀繼續(xù)執(zhí)行下去歌殃。
兩種方法結(jié)束的區(qū)別是:因拋出異常而退出的方法不會(huì)給調(diào)用他的調(diào)用者棧幀任何返回值

3、虛擬機(jī)棧的常見異常

《Java虛擬機(jī)規(guī)范》中蝙云,允許虛擬機(jī)棧的大小是動(dòng)態(tài)擴(kuò)展的或者固定不變的氓皱。
這意味著,虛擬機(jī)棧會(huì)出現(xiàn)兩種異常:
StackOverflowError異常(棧溢出):
如果線程請(qǐng)求分配的棧容量超過Java虛擬機(jī)的最大容量時(shí)勃刨,就會(huì)拋出棧溢出異常波材。最簡(jiǎn)單的示例就是不停遞歸而不退出,就會(huì)報(bào) StackOverflowError身隐。
當(dāng)虛擬機(jī)棧的大小為固定時(shí)廷区,易出現(xiàn)該異常。

OutOfMemoryError異常(內(nèi)存溢出)
當(dāng)虛擬機(jī)棧采用動(dòng)態(tài)擴(kuò)展時(shí)抡医,一定程度上可以規(guī)避 StackOverflowError躲因,但是虛擬機(jī)給每個(gè)線程分配到的內(nèi)存空間是有限的早敬,這也意味著隸屬于線程的虛擬機(jī)棧的內(nèi)存也是有限的忌傻,所以當(dāng)虛擬機(jī)棧請(qǐng)求擴(kuò)展的內(nèi)存大小無法滿足時(shí),就會(huì)報(bào)該異常搞监。
但是并不意味著水孩,采用固定大小的虛擬機(jī)棧就不會(huì)報(bào)該異常,如果某個(gè)棧幀太大琐驴,超出虛擬機(jī)棧所有的內(nèi)存也是可能的俘种。

四秤标、本地方法棧

(1)、本地方法
一個(gè)Native Method就是一個(gè)Java調(diào)用非Java代碼的接口宙刘。在定義一個(gè)本地方法時(shí)苍姜,并不提供實(shí)現(xiàn)體(有些就像定義一個(gè)Java interface),因?yàn)閷?shí)現(xiàn)體是由非Java語言在外面實(shí)現(xiàn)的悬包。本地接口的作用是融合不同的編程語言為Java所用衙猪。
(2)、本地方法棧
虛擬機(jī)棧是用于管理Java方法的調(diào)用布近,本地方法棧是用于管理本地方法的調(diào)用垫释。
①本地方法棧是線程私有的
②本地方法棧也是可實(shí)現(xiàn)固定或者可動(dòng)態(tài)擴(kuò)展的內(nèi)存大小(這也意味著本地方法棧的異常也與虛擬機(jī)棧一致)
③本地方法棧存放著被native關(guān)鍵字標(biāo)記的方法撑瞧,在執(zhí)行引擎執(zhí)行時(shí)加載本地方法庫棵譬。
④并不是所有的JVM都支持本地方法,故也無需實(shí)現(xiàn)本地方法棧
⑤在HotSpot JVM中预伺,直接將本地方法和虛擬機(jī)棧合二為一订咸。

五、擴(kuò)展--設(shè)置棧內(nèi)存大小

有代碼如下:

public class StackOverError {
    private static int count = 1;
    public static void main(String[] args) {
        System.out.println(count);
        count++;
        main(args);
    }
}

在代碼中采用了遞歸扭屁,以count++的次數(shù)來查看目前虛擬機(jī)棧的深度算谈。其結(jié)果如下:

在這里插入圖片描述

可見默認(rèn)情況下,count的值為2467料滥。
我們可以使用
-Xss:來規(guī)定了每個(gè)線程虛擬機(jī)棧的內(nèi)存大小
如下然眼,
在這里插入圖片描述

設(shè)置棧的大小:--Xss1m或者-Xss1k (設(shè)置方法IDEA:Run --> Edit Configuration --> VM option)

結(jié)果如下葵腹,

在這里插入圖片描述

可見當(dāng)設(shè)置虛擬機(jī)棧的內(nèi)存大小為2m時(shí)高每,count的值為233424。

故可看出使用參數(shù) -Xss可以設(shè)置線程的最大椉纾空間鲸匿,且虛擬機(jī)棧的大小直接決定了函數(shù)調(diào)用的大小。

參考:
知乎-JVM虛擬機(jī)棧執(zhí)行原理深入詳解

2020最新-JVM-Java虛擬機(jī)-從入門到精通-尚硅谷

知乎-Java虛擬機(jī)—棧幀阻肩、操作數(shù)棧和局部變量表

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末带欢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子烤惊,更是在濱河造成了極大的恐慌乔煞,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,686評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柒室,死亡現(xiàn)場(chǎng)離奇詭異渡贾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)雄右,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,668評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門空骚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纺讲,“玉大人,你說我怎么就攤上這事囤屹“旧酰” “怎么了?”我有些...
    開封第一講書人閱讀 158,160評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵肋坚,是天一觀的道長(zhǎng)则涯。 經(jīng)常有香客問我,道長(zhǎng)冲簿,這世上最難降的妖魔是什么粟判? 我笑而不...
    開封第一講書人閱讀 56,736評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮峦剔,結(jié)果婚禮上档礁,老公的妹妹穿的比我還像新娘。我一直安慰自己吝沫,他們只是感情好呻澜,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,847評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惨险,像睡著了一般羹幸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辫愉,一...
    開封第一講書人閱讀 50,043評(píng)論 1 291
  • 那天栅受,我揣著相機(jī)與錄音,去河邊找鬼恭朗。 笑死屏镊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的痰腮。 我是一名探鬼主播而芥,決...
    沈念sama閱讀 39,129評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼膀值!你這毒婦竟也來了棍丐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,872評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤沧踏,失蹤者是張志新(化名)和其女友劉穎歌逢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悦冀,經(jīng)...
    沈念sama閱讀 44,318評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡趋翻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,645評(píng)論 2 327
  • 正文 我和宋清朗相戀三年睛琳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盒蟆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踏烙。...
    茶點(diǎn)故事閱讀 38,777評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖历等,靈堂內(nèi)的尸體忽然破棺而出讨惩,到底是詐尸還是另有隱情,我是刑警寧澤寒屯,帶...
    沈念sama閱讀 34,470評(píng)論 4 333
  • 正文 年R本政府宣布荐捻,位于F島的核電站,受9級(jí)特大地震影響寡夹,放射性物質(zhì)發(fā)生泄漏处面。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,126評(píng)論 3 317
  • 文/蒙蒙 一菩掏、第九天 我趴在偏房一處隱蔽的房頂上張望魂角。 院中可真熱鬧,春花似錦智绸、人聲如沸野揪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,861評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽斯稳。三九已至,卻和暖如春迹恐,著一層夾襖步出監(jiān)牢的瞬間挣惰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評(píng)論 1 267
  • 我被黑心中介騙來泰國打工殴边, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留通熄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,589評(píng)論 2 362
  • 正文 我出身青樓找都,卻偏偏與公主長(zhǎng)得像唇辨,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子能耻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,687評(píng)論 2 351