摘要:對(duì)于執(zhí)行引擎來(lái)說(shuō),在活動(dòng)線(xiàn)程中,只有位于棧頂?shù)臈攀亲钣行У姆Q(chēng)為當(dāng)前棧幀與這個(gè)棧幀相關(guān)聯(lián)的方法稱(chēng)為當(dāng)前方法。執(zhí)行引擎運(yùn)行的所有的字節(jié)碼指令都只針對(duì)當(dāng)前棧幀進(jìn)行操作。
棧幀數(shù)據(jù)結(jié)構(gòu)
棧幀(Stack Frame)是用來(lái)支持虛擬機(jī)進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu),它是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)中的虛擬機(jī)棧的棧元素。
棧幀(Stack Frame)存儲(chǔ)了方法的局部變量表、操作數(shù)棧、動(dòng)態(tài)連接、和方法返回地址、額外的附加信息。
每個(gè)方法在執(zhí)行的同時(shí),都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame)。每一個(gè)方法從調(diào)用開(kāi)始至執(zhí)行完成的過(guò)程,都對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧里面從入棧到出棧的過(guò)程。
棧幀的內(nèi)存分配在編譯程序代碼的時(shí)候,棧幀中需要多大的局部變量表,多深的操作數(shù)棧都已經(jīng)完全確定了,并且寫(xiě)入到方法表的Code屬性中了,因此一個(gè)棧幀需要分配多少內(nèi)存,不會(huì)受到程序運(yùn)行期變量數(shù)據(jù)的影響,而僅僅取決于具體虛擬機(jī)的實(shí)現(xiàn)。
當(dāng)前棧幀(Current Stack Frame)一個(gè)線(xiàn)程中的方法調(diào)用鏈可能會(huì)很長(zhǎng),很多方法都同時(shí)處于執(zhí)行狀態(tài)。對(duì)于執(zhí)行引擎來(lái)說(shuō),在活動(dòng)線(xiàn)程中,只有位于棧頂?shù)臈攀亲钣行У?稱(chēng)為當(dāng)前棧幀(Current Stack Frame),與這個(gè)棧幀相關(guān)聯(lián)的方法稱(chēng)為當(dāng)前方法。執(zhí)行引擎運(yùn)行的所有的字節(jié)碼指令都只針對(duì)當(dāng)前棧幀進(jìn)行操作。在概念模型上,典型的棧幀結(jié)構(gòu)圖如下:
局部變量表局部變量表(Local Variable Table)是一組變量值存貯空間,用于存放方法參數(shù)和方法內(nèi)定義的局部變量。在Java程序編譯為Class文件時(shí)候,就在方法的Code屬性的max_locals數(shù)據(jù)項(xiàng)中確定了該方法所需要分配的局部變量表的最大容量。單位為Slot
局部變量表的容量以變量槽(Variable Slot)為最小單位。每個(gè)變量槽都可以存儲(chǔ)32位長(zhǎng)度的內(nèi)存空間,例如boolean、byte、char、short、int、float、reference。
對(duì)于64位長(zhǎng)度的數(shù)據(jù)類(lèi)型(long,double),虛擬機(jī)會(huì)以高位對(duì)齊方式為其分配兩個(gè)連續(xù)的Slot空間,也就是相當(dāng)于把一次long和double數(shù)據(jù)類(lèi)型讀寫(xiě)分割成為兩次32位讀寫(xiě)。
整型 | 字節(jié)(b) | bite(位) |
---|---|---|
byte | 1 | 1*8 |
short | 2 | 2*8 |
int | 4 | 4*8 |
long | 8 | 8*8 |
浮點(diǎn)型 | 字節(jié)(b) | bite(位) |
---|---|---|
float | 4 | 4*8 |
double | 8 | 8*8 |
char類(lèi)型 | 字節(jié)(b) | bite(位) |
---|---|---|
char | 2 | 2*8 |
布爾型 | 字節(jié)(b) | bite(位) |
---|---|---|
boolean | 1 | 1*8 |
其中:8bit=1b [一個(gè)Byte等于8個(gè)bit(位)],1024b=1kb
為了節(jié)省棧幀空間,局部變量表中的Slot是可以重用的,方法體中定義的變量,其作用域并不一定會(huì)覆蓋整個(gè)方法體,如果當(dāng)前字節(jié)碼PC計(jì)數(shù)器的值已經(jīng)超過(guò)了某個(gè)變量的作用域,那么這個(gè)變量對(duì)應(yīng)的Slot就可以交給其他變量使用。
優(yōu)點(diǎn) : 節(jié)省棧幀空間。
缺點(diǎn) : 影響到系統(tǒng)的垃圾收集行為。(如大方法占用較多的Slot,執(zhí)行完該方法的作用域后沒(méi)有對(duì)Slot賦值或者清空設(shè)置null值,垃圾回收器便不能及時(shí)的回收該內(nèi)存。)
操作數(shù)棧(operand Stack)也常稱(chēng)為操作棧,它是一個(gè)后入先出棧。和局部變量表一樣,操作數(shù)棧的最大深度也在編譯的時(shí)候?qū)懭氲紺ode屬性的max_stacks中。
操作數(shù)棧的每一個(gè)元素可用是任意的Java數(shù)據(jù)類(lèi)型,包括long和double。32位數(shù)據(jù)類(lèi)型所占的棧容量為1,64位數(shù)據(jù)類(lèi)型占用的棧容量為2。
當(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ù)傳遞的。
動(dòng)態(tài)連接每個(gè)棧幀都包含一個(gè)指向運(yùn)行時(shí)常量池中該棧幀所屬方法的引用,持有這個(gè)引用是為了支持方法調(diào)用過(guò)程中的動(dòng)態(tài)連接(Dynamic Linking)。
Class文件的常量池中存在大量符號(hào)引用,字節(jié)碼中的方法調(diào)用指令就以常量池中指向方法的符號(hào)引用作為參數(shù),這些符號(hào)引用一部分在類(lèi)加載階段中的解析階段會(huì)轉(zhuǎn)為直接引用,這種轉(zhuǎn)化也稱(chēng)為靜態(tài)解析。另外的一部分將在每一次運(yùn)行時(shí)期轉(zhuǎn)化為直接引用。這部分稱(chēng)為動(dòng)態(tài)連接。
方法返回地址當(dāng)一個(gè)方法開(kāi)始執(zhí)行后,只有2種方式可以退出這個(gè)方法 :
方法返回指令 : 執(zhí)行引擎遇到一個(gè)方法返回的字節(jié)碼指令,這時(shí)候有可能會(huì)有返回值傳遞給上層的方法調(diào)用者,這種退出方式稱(chēng)為正常完成出口。
異常退出 : 在方法執(zhí)行過(guò)程中遇到了異常,并且沒(méi)有處理這個(gè)異常,就會(huì)導(dǎo)致方法退出。
無(wú)論采用任何退出方式,在方法退出之后,都需要返回到方法被調(diào)用的位置,程序才能繼續(xù)執(zhí)行,方法返回時(shí)可能需要在棧幀中保存一些信。用來(lái)幫助恢復(fù)它的上層方法的執(zhí)行狀態(tài)。
方法退出的過(guò)程實(shí)際上就等于把當(dāng)前棧幀出棧,因此退出可能執(zhí)行的操作有:
1.恢復(fù)上層方法的局部變量表和操作數(shù)棧
2.把返回值(如果存在返回值)壓入調(diào)用者棧幀的操作數(shù)棧中
3.調(diào)整PC計(jì)數(shù)器的值以指向方法調(diào)用指令后面的一條指令
虛擬機(jī)規(guī)范允許具體的虛擬機(jī)實(shí)現(xiàn)增強(qiáng)一些規(guī)范里沒(méi)有描述的信息到棧幀之中,例如與調(diào)試相關(guān)的信息,這部分信息取決于具體的虛擬機(jī)實(shí)現(xiàn)。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75883.html
摘要:在本文,筆者將與大家概覽的體系結(jié)構(gòu)與工作方式。將第條和第條指令分別是將兩個(gè)局部變量入棧,然后相加。最后一條指令是,這條指令執(zhí)行完后當(dāng)前的這個(gè)方法對(duì)應(yīng)的這些部件會(huì)被回收,局部變量區(qū)的所有值將全部釋放,寄存器會(huì)被銷(xiāo)魂,在棧中與這個(gè)方 Java之所以號(hào)稱(chēng)一次編譯,到處運(yùn)行,主要原因是JVM屏蔽了各個(gè)計(jì)算機(jī)平臺(tái)相關(guān)的軟件(大多指系統(tǒng))或者硬件之間的差異,使得與平臺(tái)相關(guān)的耦合統(tǒng)一由JVM提供者來(lái)...
摘要:每個(gè)棧幀中包括局部變量表用來(lái)存儲(chǔ)方法中的局部變量非靜態(tài)變量函數(shù)形參。操作數(shù)棧虛擬機(jī)的解釋執(zhí)行引擎被稱(chēng)為基于棧的執(zhí)行引擎,其中所指的棧就是指操作數(shù)棧。指向運(yùn)行時(shí)常量池的引用存儲(chǔ)程序執(zhí)行時(shí)可能用到常量的引用。 本篇文章轉(zhuǎn)自微信公眾號(hào):Java后端技術(shù) 學(xué)過(guò)Java基礎(chǔ)的人都知道:值傳遞和引用傳遞是初次接觸Java時(shí)的一個(gè)難點(diǎn),有時(shí)候記得了語(yǔ)法卻記不得怎么實(shí)際運(yùn)用,有時(shí)候會(huì)的了運(yùn)用卻解釋不出...
摘要:操作數(shù)棧虛擬機(jī)的解釋執(zhí)行引擎被稱(chēng)為基于棧的執(zhí)行引擎,其中所指的棧就是指操作數(shù)棧?;緮?shù)據(jù)類(lèi)型的靜態(tài)變量前面提到方法區(qū)用來(lái)存儲(chǔ)一些共享數(shù)據(jù),因此基本數(shù)據(jù)類(lèi)型的靜態(tài)變量名以及值存儲(chǔ)于方法區(qū)的運(yùn)行時(shí)常 本文旨在用最通俗的語(yǔ)言講述最枯燥的基本知識(shí) 學(xué)過(guò)Java基礎(chǔ)的人都知道:值傳遞和引用傳遞是初次接觸Java時(shí)的一個(gè)難點(diǎn),有時(shí)候記得了語(yǔ)法卻記不得怎么實(shí)際運(yùn)用,有時(shí)候會(huì)的了運(yùn)用卻解釋不出原理,而...
摘要:虛擬機(jī)在執(zhí)行程序的過(guò)程中會(huì)把它所管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū)域。棧幀棧幀是用于支持虛擬機(jī)進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu),它是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)中的虛擬機(jī)棧的棧元素。棧幀的概念結(jié)構(gòu)如下運(yùn)行時(shí)數(shù)據(jù)區(qū)腦圖高 這里我們先說(shuō)句題外話(huà),相信大家在面試中經(jīng)常被問(wèn)到介紹Java內(nèi)存模型,我在面試別人時(shí)也會(huì)經(jīng)常問(wèn)這個(gè)問(wèn)題。但是,往往都會(huì)令我比較尷尬,我還話(huà)音未落,面試者就會(huì)背誦一段(Java虛擬...
摘要:內(nèi)存模型首先介紹下程序具體執(zhí)行的過(guò)程源代碼文件后綴會(huì)被編譯器編譯為字節(jié)碼文件后綴由中的類(lèi)加載器加載各個(gè)類(lèi)的字節(jié)碼文件,加載完畢之后,交由執(zhí)行引擎執(zhí)行在整個(gè)程序執(zhí)行過(guò)程中,會(huì)用一段空間來(lái)存儲(chǔ)程序執(zhí)行期間需要用到的數(shù)據(jù)和相關(guān)信息,這段空間一般被 [TOC] JVM內(nèi)存模型 首先介紹下Java程序具體執(zhí)行的過(guò)程: Java源代碼文件(.java后綴)會(huì)被Java編譯器編譯為字節(jié)碼文件(....
閱讀 1305·2023-04-25 19:33
閱讀 1185·2021-10-21 09:39
閱讀 3658·2021-09-09 09:32
閱讀 2636·2019-08-30 10:58
閱讀 1648·2019-08-29 16:17
閱讀 892·2019-08-29 15:29
閱讀 2906·2019-08-26 11:55
閱讀 2672·2019-08-26 10:33