Java虛擬機(jī) —— 運(yùn)行時(shí)數(shù)據(jù)區(qū)

Java虛擬機(jī)內(nèi)存尝江,是指JVM的運(yùn)行時(shí)數(shù)據(jù)區(qū)域涉波,主要分為:方法區(qū)、堆炭序、虛擬機(jī)棧啤覆、本地方法棧、程序計(jì)數(shù)器惭聂。其中方法區(qū)和堆為索引線程的共享數(shù)據(jù)區(qū)窗声,而虛擬機(jī)棧、本地方法棧彼妻、程序計(jì)數(shù)器為線程隔離的數(shù)據(jù)區(qū)嫌佑。


程序計(jì)數(shù)器

每個(gè)線程都有一個(gè)獨(dú)立的計(jì)數(shù)器用來(lái)記錄程序當(dāng)前執(zhí)行的指令豆茫,可以看成是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器。如果線程正在執(zhí)行Java方法屋摇,計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址揩魂;如果執(zhí)行的是Native方法,計(jì)數(shù)器記錄值為空(Undefined)炮温。程序計(jì)數(shù)器占用的內(nèi)存空間非常小火脉,是線程的私有區(qū)域,此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒(méi)有規(guī)定任何OutOfMemoryError情況的區(qū)域柒啤。

虛擬機(jī)棧

虛擬機(jī)棧也是線程私有的倦挂,它的生命周期與線程相同。虛擬機(jī)棧是一個(gè)后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)担巩,里面存放的是棧幀方援,每個(gè)Java方法的調(diào)用對(duì)應(yīng)一個(gè)棧幀在虛擬機(jī)棧中的入棧和出棧。當(dāng)線程執(zhí)行一個(gè)Java方法執(zhí)行時(shí)涛癌,就會(huì)創(chuàng)建一個(gè)新的棧幀并壓入到該線程的虛擬機(jī)棧的棧頂犯戏,Java方法執(zhí)行結(jié)束后棧頂?shù)脑摋蜁?huì)彈出棧并銷(xiāo)毀。

棧幀里面存放的是Java方法執(zhí)行的一些數(shù)據(jù)拳话,包括局部變量表先匪、操作數(shù)棧、動(dòng)態(tài)連接弃衍、方法出口等呀非。


局部變量表

局部變量表是一組變量值存儲(chǔ)空間,用于存放方法參數(shù)和方法內(nèi)部定義的局部變量镜盯。

局部變量表的容量以變量槽(Slot)為最小單位岸裙,32位虛擬機(jī)中一個(gè)Slot可以存放一個(gè)32位以內(nèi)的數(shù)據(jù)類型(boolean、byte形耗、char哥桥、short、int激涤、float、reference和returnAddress八種)判呕。reference類型虛擬機(jī)規(guī)范沒(méi)有明確說(shuō)明它的長(zhǎng)度倦踢,但一般來(lái)說(shuō),虛擬機(jī)實(shí)現(xiàn)至少都應(yīng)當(dāng)能從此引用中直接或者間接地查找到對(duì)象在Java堆中的起始地址索引和方法區(qū)中的對(duì)象類型數(shù)據(jù)侠草。returnAddress類型是為字節(jié)碼指令jsr辱挥、jsr_w和ret服務(wù)的,它指向了一條字節(jié)碼指令的地址边涕。

Java虛擬機(jī)是使用局部變量表完成參數(shù)值到Java方法參數(shù)變量列表的傳遞過(guò)程的晤碘,如果是實(shí)例方法(非static)褂微,那么局部變量表的第0位索引的Slot默認(rèn)是用于傳遞方法所屬對(duì)象實(shí)例的引用,在方法中通過(guò)this訪問(wèn)园爷。

Slot是可以重用的宠蚂,下一次分配Slot的時(shí)候,將會(huì)覆蓋原來(lái)的數(shù)據(jù)童社。Slot對(duì)對(duì)象的引用會(huì)影響GC(要是被引用求厕,將不會(huì)被回收)。

操作數(shù)棧

操作數(shù)棧也常被稱為操作棧扰楼,同樣是一個(gè)后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)呀癣。當(dāng)一個(gè)方法剛剛開(kāi)始執(zhí)行的時(shí)候,這個(gè)方法的操作數(shù)棧是空的弦赖,在方法的執(zhí)行過(guò)程中项栏,會(huì)有各種字節(jié)碼指令向操作數(shù)棧中寫(xiě)入和提取內(nèi)容,也就是入棧出棧操作蹬竖。在做算術(shù)運(yùn)算的時(shí)候是通過(guò)操作數(shù)棧來(lái)進(jìn)行的沼沈,在調(diào)用其他方法的時(shí)候是通過(guò)操作數(shù)棧來(lái)進(jìn)行參數(shù)傳遞的。JVM將操作數(shù)棧作為工作區(qū)案腺。JVM沒(méi)有寄存器庆冕,所有的參數(shù)傳遞和返回值都是基于操作數(shù)棧來(lái)完成的。

比如劈榨,執(zhí)行引擎執(zhí)行c = a + b時(shí)访递,會(huì)先被操作的參數(shù)ab壓入操作數(shù)棧,然后操作指令將他們彈出棧同辣,并執(zhí)行操作拷姿,將結(jié)果再壓入棧。

Java虛擬機(jī)的解釋執(zhí)行引擎稱為“基于棧的執(zhí)行引擎”旱函,其中所指的“椣斐玻”就是操作數(shù)棧。

動(dòng)態(tài)連接

每個(gè)棧幀都包含一個(gè)指向運(yùn)行時(shí)常量池中該棧幀所屬方法的引用棒妨,持有這個(gè)引用是為了支持方法調(diào)用過(guò)程中的動(dòng)態(tài)連接踪古。Class文件的常量池有存有大量的符號(hào)引用,字節(jié)碼中的方法調(diào)用指令就以常量池中指向方法的符號(hào)引用為參數(shù)券腔。這些符號(hào)引用一部分會(huì)在類加載階段或第一次使用的時(shí)候轉(zhuǎn)化為直接引用伏穆,這種轉(zhuǎn)化稱為靜態(tài)解析。另外一部分將在每一次的運(yùn)行期間轉(zhuǎn)化為直接引用纷纫,這部分稱為動(dòng)態(tài)連接枕扫。

方法出口(返回地址)

當(dāng)一個(gè)方法被執(zhí)行后,有兩種方式退出這個(gè)方法辱魁。第一種方式是執(zhí)行引擎遇到任意一個(gè)方法返回的字節(jié)碼指令烟瞧,這時(shí)候可能會(huì)有返回值傳遞給上層的方法調(diào)用者(調(diào)用當(dāng)前方法的方法稱為調(diào)用者)诗鸭,是否有返回值和返回值的類型將根據(jù)遇到何種方法返回指令來(lái)決定,這種退出方法的方式稱為正常完成出口(Normal Method Invocation Completion)参滴。

另外一種退出方式是强岸,在方法執(zhí)行過(guò)程中遇到了異常,并且這個(gè)異常沒(méi)有在方法體內(nèi)得到處理卵洗,無(wú)論是Java虛擬機(jī)內(nèi)部產(chǎn)生的異常请唱,還是代碼中使用athrow字節(jié)碼指令產(chǎn)生的異常,只要在本方法的異常表中沒(méi)有搜索到匹配的異常處理器过蹂,就會(huì)導(dǎo)致方法退出十绑,這種退出方法的方式稱為異常完成出口(Abrupt Method Invocation Completion)。一個(gè)方法使用異常完成出口的方式退出酷勺,是不會(huì)給它的上層調(diào)用者產(chǎn)生任何返回值的本橙。

無(wú)論采用何種退出方式,在方法退出之后脆诉,都需要返回到方法被調(diào)用的位置甚亭,程序才能繼續(xù)執(zhí)行,方法返回時(shí)可能需要在棧幀中保存一些信息击胜,用來(lái)幫助恢復(fù)它的上層方法的執(zhí)行狀態(tài)亏狰。一般來(lái)說(shuō),方法正常退出時(shí)偶摔,調(diào)用者的PC計(jì)數(shù)器的值就可以作為返回地址暇唾,棧幀中很可能會(huì)保存這個(gè)計(jì)數(shù)器值。而方法異常退出時(shí)辰斋,返回地址是要通過(guò)異常處理器來(lái)確定的策州,棧幀中一般不會(huì)保存這部分信息。

方法退出的過(guò)程實(shí)際上等同于把當(dāng)前棧幀出棧宫仗,因此退出時(shí)可能執(zhí)行的操作有:恢復(fù)上層方法的局部變量表和操作數(shù)棧够挂,把返回值(如果有的話)壓入調(diào)用者棧幀的操作數(shù)棧中,調(diào)整PC計(jì)數(shù)器的值以指向方法調(diào)用指令后面的一條指令等藕夫。

虛擬機(jī)棧Error

Java虛擬機(jī)棧有可能出現(xiàn)的error就是StackOverflowErrorOutOfMemoryError孽糖。當(dāng)線程請(qǐng)求的棧深度大于Java虛擬機(jī)棧允許的深度時(shí),就會(huì)拋出StackOverflowError錯(cuò)誤毅贮。比如將一個(gè)方法反復(fù)遞歸梭姓,最終就會(huì)出現(xiàn)StackOverflowError。當(dāng)Java虛擬機(jī)椖勐耄可以動(dòng)態(tài)擴(kuò)展時(shí)(大部分的 Java 虛擬機(jī)都可動(dòng)態(tài)擴(kuò)展,不過(guò) Java 虛擬機(jī)規(guī)范中也允許固定長(zhǎng)度的虛擬機(jī)棧)罪既,如果無(wú)法申請(qǐng)到足夠的內(nèi)存來(lái)擴(kuò)展棧铸题,就會(huì)拋出OutOfMemoryError錯(cuò)誤铡恕。

本地方法棧

本地方法棧與虛擬機(jī)棧的功能類似,他們的區(qū)別在于虛擬機(jī)棧為執(zhí)行Java代碼方法服務(wù)丢间,而本地方法棧是為Native方法服務(wù)探熔。本地方法棧就是一個(gè)C的方法棧,本地方法棧的參數(shù)順序烘挫、返回值和典型的C程序相同诀艰,本地方法一般來(lái)說(shuō)可以(依賴 JVM 的實(shí)現(xiàn))反過(guò)來(lái)調(diào)用 JVM 中的 Java 方法。這種native方法調(diào)用Java會(huì)發(fā)生在棧(一般是Java棧)上饮六,線程將離開(kāi)本地方法棧其垄,并在 Java 棧上開(kāi)辟一個(gè)新的棧幀。

與虛擬機(jī)棧一樣卤橄,本地方法棧也會(huì)拋出StackOverflowErrorOutOfMemoryError绿满。

堆是Java虛擬機(jī)中最大的一塊內(nèi)存區(qū)域,它是有所有的線程共享窟扑。幾乎所有的實(shí)例對(duì)象和數(shù)組都是在堆中存放喇颁。只要是通過(guò)new關(guān)鍵字創(chuàng)建對(duì)象或者直接聲明數(shù)組,都會(huì)在堆中開(kāi)辟內(nèi)存空間來(lái)存放嚎货。因?yàn)樵跅粍?chuàng)建后無(wú)法調(diào)整大小橘霎,棧幀中只能存放對(duì)象和數(shù)組在堆中的引用。方法或線程結(jié)束時(shí)對(duì)象和數(shù)組不會(huì)立即被移除銷(xiāo)毀殖属,它只能由垃圾回收器回收姐叁。

同樣地,如果在堆中沒(méi)有內(nèi)存來(lái)完成實(shí)例分配忱辅,并且堆也無(wú)法擴(kuò)展時(shí)七蜘,將會(huì)拋出OutOfMemoryError

方法區(qū)

方法區(qū)與堆一樣墙懂,是各個(gè)線程共享的內(nèi)存區(qū)域橡卤,它存儲(chǔ)已經(jīng)被虛擬機(jī)加載的類信息(包括字段信息、方法信息损搬、方法代碼等)碧库、常量、靜態(tài)變量巧勤、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)嵌灰。

方法區(qū)中的內(nèi)存一般不會(huì)被GC回收,GC也很難回收颅悉。方法區(qū)的內(nèi)存回收主要是針對(duì)針對(duì)常量池的回收和對(duì)類的卸載沽瞭。根據(jù) Java 虛擬機(jī)規(guī)范的規(guī)定,當(dāng)方法區(qū)無(wú)法滿足內(nèi)存分配需求時(shí)剩瓶,將拋出OutOfMemoryError異常驹溃。

運(yùn)行時(shí)常量池

運(yùn)行時(shí)常量池是方法區(qū)的一部分城丧,Class文件除了有關(guān)類的版本、字段豌鹤、方法亡哄、接口等描述信息外,還有一項(xiàng)信息是常量池布疙,用于存放編譯期生成的各種字面量和符號(hào)引用蚊惯,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。運(yùn)行時(shí)常量池可以理解為是類或接口的常量池的運(yùn)行時(shí)表現(xiàn)形式灵临。


參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末截型,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子俱诸,更是在濱河造成了極大的恐慌菠劝,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睁搭,死亡現(xiàn)場(chǎng)離奇詭異赶诊,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)园骆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)舔痪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人锌唾,你說(shuō)我怎么就攤上這事锄码。” “怎么了晌涕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵滋捶,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我余黎,道長(zhǎng)重窟,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任惧财,我火速辦了婚禮巡扇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘垮衷。我一直安慰自己厅翔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布搀突。 她就那樣靜靜地躺著刀闷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涩赢,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天戈次,我揣著相機(jī)與錄音,去河邊找鬼筒扒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛绊寻,可吹牛的內(nèi)容都是我干的花墩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼澄步,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼冰蘑!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起村缸,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤祠肥,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后梯皿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體仇箱,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年东羹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了剂桥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡属提,死狀恐怖权逗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情冤议,我是刑警寧澤斟薇,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站恕酸,受9級(jí)特大地震影響堪滨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜尸疆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一椿猎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寿弱,春花似錦犯眠、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春量蕊,著一層夾襖步出監(jiān)牢的瞬間铺罢,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工残炮, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留韭赘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓势就,卻偏偏與公主長(zhǎng)得像泉瞻,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苞冯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容