[虛擬機(jī)字節(jié)碼執(zhí)行引擎]1——棧楨

本文主要內(nèi)容

  • 前言
  • 運(yùn)行時(shí)棧楨結(jié)構(gòu)

已經(jīng)學(xué)習(xí)了虛擬機(jī)內(nèi)存區(qū)域、Class文件結(jié)構(gòu)、類加載機(jī)制等知識(shí),是時(shí)候?qū)W習(xí)虛擬機(jī)字節(jié)碼執(zhí)行過程了。

前言

虛擬機(jī)是一個(gè)相對(duì)物理機(jī)而言的概念,它們都有代碼執(zhí)行能力,其區(qū)別是物理機(jī)的執(zhí)行引擎是直接建立在處理器、硬件、指令集和操作系統(tǒng)層面的。而虛擬機(jī)的執(zhí)行引擎則是自己實(shí)現(xiàn)的。

一個(gè)Java應(yīng)用程序就對(duì)應(yīng)著一個(gè)虛擬機(jī)進(jìn)程,虛擬機(jī)根據(jù)規(guī)范,自己操作內(nèi)存、解析字節(jié)碼并執(zhí)行。

運(yùn)行時(shí)棧楨結(jié)構(gòu)

棧楨是用于支持虛擬機(jī)進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu),它是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)中的虛擬機(jī)棧的棧元素。

棧楨中存儲(chǔ)了方法的局部變量表、操作數(shù)棧、動(dòng)態(tài)連接和方法返回地址等信息。

每一個(gè)方法從調(diào)用開始到執(zhí)行完成的過程,就對(duì)應(yīng)著一個(gè)棧楨在虛擬機(jī)棧里入棧到出棧的過程。

在編譯代碼的時(shí)候,棧楨中需要多大的局部變量表、多深的操作數(shù)棧都已經(jīng)完全確定了,并且寫入到方法的Code屬性了。

如上圖,stack = 1,表明操作數(shù)棧深度最大為1,locals = 1,局部變量表所需要的內(nèi)容為1個(gè)slot。

對(duì)于執(zhí)行引擎來說,活動(dòng)線程中,只有棧頂?shù)臈E是有效的,稱為當(dāng)前棧楨,這個(gè)棧楨所關(guān)聯(lián)的方法稱為當(dāng)前方法,執(zhí)行引擎所運(yùn)行的所有字節(jié)碼指令都只針對(duì)當(dāng)前棧楨進(jìn)行操作。

1、局部變量表

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

在Java程序被編譯為Class文件時(shí),就在方法的Code屬性的max_locals數(shù)據(jù)項(xiàng)中確定了該方法所需要分配的最大局部變量表的容量。

局部變量表以slot為單位,每個(gè)slot單位可以存放一個(gè)boolean,byte,char,short,int,float,reference或returnAddress類型的數(shù)據(jù)。對(duì)于64位類型的數(shù)據(jù),虛擬機(jī)會(huì)以高位在前的方式為其分配2個(gè)連續(xù)的slot空間。64位的數(shù)據(jù)類型只有兩種:long和double。

虛擬機(jī)通過索引定位方式引用局部變量表,如果是32位類型數(shù)據(jù),索引n就代表使用了第n個(gè)slot,如果是64位,則說明要使用n和n+1兩個(gè)slot

注意,如果是非static方法,局部變量表中的第0位索引的slot,默認(rèn)是用于傳遞方法所屬對(duì)象實(shí)例的引用,即this。

局部變量表中的slot是可以重用的,方法體中的定義的變量作用域不一定會(huì)覆蓋整個(gè)方法體,如果超出作用域,那么這個(gè)變量的slot就可以交給其它變量使用。

2、操作數(shù)棧

操作數(shù)棧也稱為操作棧,它是一個(gè)先入后出的棧。

同局部變量表一樣,操作數(shù)棧的最大深度也在編譯時(shí)候被寫入Code屬性的max_stacks屬性中了。操作數(shù)棧的每一個(gè)元素可以是任意的Java數(shù)據(jù)類型,包括long和double,32位數(shù)據(jù)類型占據(jù)棧容量為1,64位??臻g占位為2,在方法執(zhí)行的任何階段,操作數(shù)棧的深度都不會(huì)超過max_stacks。

當(dāng)一個(gè)方法開始執(zhí)行時(shí),操作數(shù)棧是空的。

舉個(gè)例子說明操作數(shù)棧是如何工作的。整數(shù)加法的字節(jié)碼指令iadd在運(yùn)行的時(shí)候,要求操作數(shù)棧中最接近棧頂?shù)膬蓚€(gè)元素已經(jīng)存入了int值,當(dāng)執(zhí)行此命令時(shí),會(huì)將這兩個(gè)int值出棧并相加,然后將結(jié)果入棧。

Java虛擬機(jī)被稱為“基于棧的執(zhí)行引擎”,其中所指的棧就是操作數(shù)棧。Android虛擬機(jī)則是基于寄存器的虛擬引擎。

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

每一個(gè)棧楨中都包含一個(gè)指向運(yùn)行時(shí)常量池中該棧楨所屬方法的引用,持有這個(gè)引用是為了支持方法調(diào)用過程中的動(dòng)態(tài)連接。

Class文件加載過程中,有一個(gè)解析階段,將符號(hào)引用轉(zhuǎn)化為直接引用,但這個(gè)轉(zhuǎn)化不是完全體的,只轉(zhuǎn)化了一部分,還有一部分需要在運(yùn)行時(shí)靠動(dòng)態(tài)連接來完成。

Java三大特性中的多態(tài)就是依靠動(dòng)態(tài)連接完成的,如果在編譯期間就完全轉(zhuǎn)化成直接引用,那多態(tài)就不會(huì)出現(xiàn)了。關(guān)于這點(diǎn)在方法調(diào)用那節(jié)再談。

4、方法返回地址

當(dāng)一個(gè)方法執(zhí)行后,有兩種方式退出方法

  • 執(zhí)行引擎遇到任何一個(gè)方法返回的字節(jié)碼指令。
  • 方法執(zhí)行過程中遇到了異常,并且異常沒有在方法中得到處理。

無論是何種退出方式,方法退出后,都需要返回到方法被調(diào)用的位置,程序才能繼續(xù)執(zhí)行。調(diào)用者的PC計(jì)數(shù)器的值就可以作為返回地址,棧楨中很可能會(huì)保存這個(gè)計(jì)數(shù)器值。

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

關(guān)于方法調(diào)用、字節(jié)碼詳細(xì)解析過程,下一篇文章繼續(xù)分析。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容