日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

java虚拟机调用linux_Java虚拟机字节码执行引擎

發(fā)布時間:2024/10/8 linux 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java虚拟机调用linux_Java虚拟机字节码执行引擎 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

定義

Java虛擬機(jī)字節(jié)碼執(zhí)行引擎是jvm最核心的組成部分之一,它做的事情很簡單:輸入的是字節(jié)碼文件,處理過程是字節(jié)碼解析的等效過程,輸出的是執(zhí)行結(jié)果。在不同的虛擬機(jī)實(shí)現(xiàn)里,執(zhí)行引擎在執(zhí)行java代碼的時候可能會有解釋執(zhí)行和編譯執(zhí)行兩種選擇,也可能兩者兼?zhèn)洹?/p>

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

java字節(jié)碼執(zhí)行引擎在調(diào)用和執(zhí)行方法的時候使用了一種叫做棧幀的數(shù)據(jù)結(jié)構(gòu)。

在jvm的內(nèi)存結(jié)構(gòu)里,存在這一塊稱為虛擬機(jī)棧的內(nèi)存區(qū)域,虛擬機(jī)棧中的元素就是棧幀。每個方法的調(diào)用至結(jié)束對應(yīng)著一個棧幀在虛擬機(jī)棧的入棧和出棧。

棧幀數(shù)據(jù)結(jié)構(gòu)示意圖:

如圖所示,因?yàn)樘摂M機(jī)棧是線程私有的,所以每個線程都有自己的虛擬機(jī)棧;而每個線程的虛擬機(jī)棧中都有多個棧幀對應(yīng)一個方法調(diào)用鏈中的多個方法,棧頂?shù)臈钱?dāng)前正在執(zhí)行的方法;每個棧幀主要包含4個部分:

局部變量表

操作數(shù)棧

動態(tài)連接

方法返回地址

在編譯代碼的階段,棧幀中需要多大的局部變量表,多深的操作數(shù)棧都是已經(jīng)完全確定的,而且會寫入到方法表的Code屬性中。

接下來一次介紹棧幀中的4個部分:

局部變量表

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

局部變量表的容量以變量槽Slot為最小單位,規(guī)定一個Slot可以存放一個32位以內(nèi)的數(shù)據(jù)類型,java中占32位以內(nèi)的數(shù)據(jù)類型有8種基本數(shù)據(jù)除了long和double以外的6種,還有reference和returnAddress,共8種類型。而對于64位的數(shù)據(jù)類型,即long和double,則會以高位對齊的方式為其分配兩個連續(xù)的Slot空間。

在方法執(zhí)行時,方法的參數(shù)列表是通過局部變量表傳遞的,如果執(zhí)行的是實(shí)例方法(非static方法),則局部變量表中的第0位索引的Slot默認(rèn)保存方法所屬對象的引用,以支持“this”關(guān)鍵字來訪問對象數(shù)據(jù)。其余參數(shù)則按參數(shù)列表順序,占用從1開始的局部變量Slot,參數(shù)表分配完成后,在根據(jù)方法體內(nèi)局部變量定義的順序和作用域?yàn)榫植孔兞糠峙淦溆郤lot。之所以這里要強(qiáng)調(diào)作用域是因?yàn)闉榱斯?jié)省棧幀空間,局部變量表中的Slot是可以重用的,當(dāng)一個Slot中保存的局部變量超出其作用域后,這個Slot可以被復(fù)用來存儲新的變量。

操作數(shù)棧

操作數(shù)棧是用來執(zhí)行字節(jié)碼命令的,比如iadd命令在運(yùn)行的時候,就是把操作數(shù)棧中最接近棧頂?shù)?個元素出棧相加,然后將結(jié)果入棧。

操作數(shù)棧的每一個元素可以是任意的java數(shù)據(jù)類型,32位數(shù)據(jù)所占棧容量為1,64位數(shù)據(jù)所占棧容量為2。

java虛擬機(jī)的解釋執(zhí)行引擎是“基于棧的執(zhí)行引擎”,這里的棧就是指操作數(shù)棧。

動態(tài)連接

一個指向運(yùn)行時常量池的引用,用來支持當(dāng)前方法的代碼實(shí)現(xiàn)動態(tài)鏈接。

方法返回地址

一個方法開始后,只有兩種方式可以退出,一種是當(dāng)執(zhí)行引擎遇到任意一個返回字節(jié)碼指令的時候,另一種是在執(zhí)行中遇到異常并且沒有捕獲的時候。無論以哪種方式退出,退出后都需要返回到方法被調(diào)用的位置,因此需要在棧幀中保存一些信息,用來恢復(fù)它上層方法的執(zhí)行狀態(tài)。

方法調(diào)用

方法調(diào)用不等于方法執(zhí)行,方法調(diào)用階段的唯一目的就是確定被調(diào)用方法的版本。

解析

我們知道,在虛擬機(jī)進(jìn)行類加載的時候,有一個階段叫做“解析”,在解析階段會將常量池中的一部分符號引用轉(zhuǎn)化為直接引用,在這個階段,有一部分方法調(diào)用就已經(jīng)被解析為了直接引用,解析的前提是,這部分方法的調(diào)用目標(biāo)在編譯階段就可以確定下來。

在java虛擬機(jī)中共有5個方法調(diào)用指令:

invokestatic

invokespecial

invokevirtual

invokeinterface

invokedynamic

可以在“解析”階段就確認(rèn)調(diào)用目標(biāo)的有invokestatic和invokespecial指令調(diào)用的方法以及invokevirtual調(diào)用的final方法,這些方法被稱為“非虛方法”,與之相反的被稱為“虛方法”。

分派

分派分為“靜態(tài)分派”和“動態(tài)分派”。

靜態(tài)分派

靜態(tài)分派用來實(shí)現(xiàn)java語言的"重載"特性。

當(dāng)我們聲明一個變量時,可能會用到這種形式:Human man=new Man();(Man是Human子類),對于man這個變量來說,Human叫做它的靜態(tài)類型,Man叫做它的實(shí)際類型。

"重載"是基于靜態(tài)類型。也就是說,對于相同名稱,參數(shù)列表不同的方法,選擇哪個方法取決于參數(shù)列表中參數(shù)的靜態(tài)類型,而靜態(tài)類型在編譯時是可知的,因此靜態(tài)分派發(fā)生在編譯階段。

動態(tài)分派

動態(tài)分派用來實(shí)現(xiàn)java語言的“重寫”特性,動態(tài)分派對應(yīng)invokevirtual命令。

invokevirtual命令的執(zhí)行過程如下:

找到操作數(shù)棧棧頂?shù)牡谝粋€元素所指向的對象(也被稱為方法的接收者)實(shí)際類型,記為C。

如果在C中找到與常量中的描述符和簡單名稱都相符的方法,則進(jìn)行訪問權(quán)限驗(yàn)證,如果通過則返回這個方法的直接引用,查找結(jié)束;如果不通過,則返回java.lang.IllegalAccessError異常。

否則,按照繼承關(guān)系從下往上依次對C的各個父類進(jìn)行第2步的搜索和驗(yàn)證過程。

如果最終沒有找到合適的方法,則拋出java.lang.AbstractMethodError異常。

我們可以看出invokevirtual指令執(zhí)行的時候是由對象的實(shí)際類型決定調(diào)用哪個方法版本的,而對象的實(shí)際類型只有到運(yùn)行期的時候才能確定,我們把這種在運(yùn)行期確定方法版本的分派過程稱為“動態(tài)分派”。

單分派和多分派

方法的接收者與方法的參數(shù)統(tǒng)稱為方法的宗量,根據(jù)分派基于宗量的多少,將分派分為單分派和多分派兩種。

目前的java語言中,靜態(tài)分派的時候,是基于接收者和方法參數(shù)兩個宗量進(jìn)行的,因此靜態(tài)分派是多分派;動態(tài)分派的時候,是基于接收者一個宗量進(jìn)行的,因此動態(tài)分派是單分派。

方法執(zhí)行

一般從程序代碼到物理機(jī)可以運(yùn)行的目標(biāo)代碼都要經(jīng)歷下圖所示的過程:

圖中展示了編譯執(zhí)行和解釋執(zhí)行這兩種路徑,無論是哪種執(zhí)行方式,一般都會先通過詞法分析和語法分析把源代碼轉(zhuǎn)化為抽象語法樹(AST)。

然后從抽象語法樹節(jié)點(diǎn)開始,中間的分支代表的是解釋執(zhí)行過程,下邊的分支代表的是編譯執(zhí)行過程。

jvm執(zhí)行字節(jié)碼是采用的解釋執(zhí)行,javac編譯器完成了程序代碼經(jīng)過詞法分析、語法分析到抽象語法樹,再遍歷語法樹生成線性的字節(jié)碼指令流的過程。而解釋器則在虛擬機(jī)的內(nèi)部。

javac編譯器輸出的字節(jié)碼指令流是一種基于棧的指令集架構(gòu),指令流中的大部分指令都是零地址指令,它們依賴操作數(shù)棧進(jìn)行工作。與之相對的是基于寄存器的指令集。

基于棧的指令集有以下優(yōu)點(diǎn):

可移植

代碼更加緊湊(不需要存放參數(shù))

編譯器實(shí)現(xiàn)更簡單(不需要考慮空間分配問題,所需空間都在棧上操作)

基于棧的指令集的主要缺點(diǎn)就是執(zhí)行速度慢:一個是因?yàn)榛跅M瓿上嗤δ芩枰闹噶罴瘮?shù)量一般比基于寄存器要多,因?yàn)槿霔?、出棧會產(chǎn)生多余的指令數(shù)量;另一個是因?yàn)闂J腔趦?nèi)存實(shí)現(xiàn)的,內(nèi)存的讀取速度本身就比處理器寄存器要慢,這一點(diǎn)可以采取棧頂緩存的手段,把最常用的操作映射到寄存器中避免直接訪問內(nèi)存,但是畢竟只是優(yōu)化措施,不能解決本質(zhì)問題。

執(zhí)行方法中的代碼本質(zhì)就是通過棧的指令集來進(jìn)行運(yùn)算,這里就不詳述了。

總結(jié)

以上是生活随笔為你收集整理的java虚拟机调用linux_Java虚拟机字节码执行引擎的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。