国产成人精品亚洲777人妖,欧美日韩精品一区视频,最新亚洲国产,国产乱码精品一区二区亚洲

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Java虛擬機(jī)執(zhí)行引擎知識(shí)總結(jié)

瀏覽:9日期:2022-08-31 17:49:07

執(zhí)行引擎

也只有幾個(gè)概念, JVM方法調(diào)用和執(zhí)行的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)是 棧幀, 是內(nèi)存區(qū)域中 虛擬機(jī)棧中的棧元素, 每一個(gè)方法的執(zhí)行就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中出棧入棧的過(guò)程.

棧幀:則是包含有局部變量表, 操作數(shù)棧, 動(dòng)態(tài)連接, 方法返回地址, 附加信息.

1 局部變量表:

存儲(chǔ)單位是 slot, 一個(gè)slot占據(jù)32位, 對(duì)于64位的數(shù)據(jù)類型, 則是分配連續(xù)兩個(gè)slot空間. 而對(duì)于一個(gè)非靜態(tài)方法而言, 有一個(gè)隱藏參數(shù), 為 this, 而在局部變量表中的變量存儲(chǔ)順序則是

this -> 方法參數(shù) -> 方法體內(nèi)的變量(slot可以重用, 超出作用域即可復(fù)用.) 方法在編譯完成后, 其所需的空間已經(jīng)確定.

(這里也是需要注意的一個(gè)地方, 變量的作用域常常會(huì)覆蓋整個(gè)方法, 即使變量已經(jīng)不再使用, 但只要還在作用域內(nèi), 其slot空間就無(wú)法給其他變量使用, 因此, 最好是在需要使用到變量時(shí), 定義在合理的作用域范圍內(nèi).)

2 操作數(shù)棧:

在操作數(shù)棧中需要注意,其數(shù)據(jù)類型必須與字節(jié)碼指令的序列嚴(yán)格匹配.

3 動(dòng)態(tài)連接: 稍后詳解

4 方法返回地址:

方法有兩種退出方式, 正常退出, 異常退出, 當(dāng)正常退出后, 會(huì)恢復(fù)上層方法的局部變量表, 操作數(shù)棧, 并把方法返回結(jié)果壓入調(diào)用者的操作數(shù)棧.

方法調(diào)用

方法調(diào)用階段的唯一目的是, 確定調(diào)用方法的版本究竟是哪一個(gè).

在Java虛擬機(jī)中提供了5條方法調(diào)用的相關(guān)指令:

invokestatic: 調(diào)用靜態(tài)方法

invokespecial: 調(diào)用實(shí)例構(gòu)造器方法, 私有方法, 父類方法

invokevirtual: 調(diào)用所有的虛方法

invokeinterface: 調(diào)用所有的接口方法

invokedynamic: 先在運(yùn)行時(shí)動(dòng)態(tài)解析出調(diào)用點(diǎn)限定符所引用的方法, 然后再執(zhí)行該方法.

虛方法是非虛方法的補(bǔ)集, 什么是非虛方法呢? 能夠在編譯器就確定將要調(diào)用的究竟是哪個(gè)方法, 進(jìn)而將該方法的符號(hào)引用 轉(zhuǎn)換為 相應(yīng)的直接引用的 方法就被稱作非虛方法.

我們知道在類加載時(shí), 在相應(yīng)的類信息中, 存有對(duì)應(yīng)方法的相關(guān)信息, 常量池中存有相關(guān)直接引用. 在類加載的解析階段, 即會(huì)將這部分的符號(hào)引用轉(zhuǎn)換為直接引用.

那么什么方法才滿足這種條件呢?

能夠被invokespecial 和 invokestatic指令調(diào)用的方法, 都是可以在編譯器確定的方法, 即靜態(tài)方法, 私有方法, 父類方法(super.), 實(shí)例構(gòu)造器.

在final方法是個(gè)特殊點(diǎn), 雖然final方法的執(zhí)行為 invokevirtual, 但它依然屬于非虛方法, 不難理解, final方法不能夠被重寫(xiě).

方法分派(dispatch)

1 靜態(tài)分派

對(duì)于代碼

Human man = new Man();

其中Human被稱為變量的靜態(tài)類型, 也叫外觀類型, 而 Man則是變量的實(shí)際類型. 而一個(gè)變量的靜態(tài)類型, 在聲明時(shí)即已經(jīng)確定, 僅僅在使用時(shí)才能夠臨時(shí)轉(zhuǎn)換靜態(tài)類型, 但變量本身的靜態(tài)類型并不會(huì)改變, 實(shí)際類型的變化只有在運(yùn)行期才能確定.

//實(shí)際類型變化 Human man = new Man(); man = new Woman(); //靜態(tài)類型的變化 method((Man) man); method((Woman) man);

而當(dāng)我們?cè)谥剌d方法時(shí), 向方法中傳入的參數(shù)類型, 即是靜態(tài)類型.因此 重載是一種 可以在編譯期就被確定執(zhí)行方法版本 的行為.

2 動(dòng)態(tài)分派

動(dòng)態(tài)分派 與 重寫(xiě)息息相關(guān).

static class Human{ void sayHello() { System.out.println('human say hello'); } } static class Man extends Human{ @Override void sayHello() { System.out.println('man say hello'); } } void sayHello(Human man) { man.sayHello(); } public static void main(String[] args) { Human man = new Man(); Human human = new Human(); new Main().sayHello(man); new Main().sayHello(human); } //out: man say hello human say hello

結(jié)果不必多做解釋, 而現(xiàn)在的問(wèn)題在于, 虛擬機(jī)如何知道, 究竟調(diào)用的是哪個(gè)方法?

0: new #3 // class Main$Man 3: dup 4: invokespecial #4 // Method Main$Man.'<init>':()V 7: astore_1 8: new #5 // class Main$Human 11: dup 12: invokespecial #6 // Method Main$Human.'<init>':()V 15: astore_2 16: new #7 // class Main 19: dup 20: invokespecial #8 // Method '<init>':()V 23: aload_1 24: invokevirtual #9 // Method sayHello:(LMain$Human;)V 27: new #7 // class Main 30: dup 31: invokespecial #8 // Method '<init>':()V 34: aload_2 35: invokevirtual #9 // Method sayHello:(LMain$Human;)V 38: return

其中主要關(guān)注幾個(gè)方法的執(zhí)行點(diǎn), invokespecial不用多說(shuō), 之前提到過(guò), 是執(zhí)行 構(gòu)造器方法時(shí) 的指令

而 invokevirtual 則正是執(zhí)行 main.sayHello(), 方法的指令, 指令的運(yùn)行時(shí)解析過(guò)程大致如下:

而其中的關(guān)鍵點(diǎn)就在于, 取到的是 對(duì)象的實(shí)際類型.

1 找到操作數(shù)棧頂?shù)牡谝粋€(gè)元素的所指對(duì)象的實(shí)際類型, 記做C

2 如果在C中找到與描述符 和 簡(jiǎn)單名稱都相符的方法, 進(jìn)行訪問(wèn)校驗(yàn), 如果可以則返回方法的直接引用, 否則拋出 IllegalAccessError異常

3 否則按照繼承關(guān)系 從下向上對(duì)C的各個(gè)父類進(jìn)行第二步的搜索驗(yàn)證過(guò)程.

4 如果始終找不到, 拋出異常.

動(dòng)態(tài)類型語(yǔ)言

這也是要提到的關(guān)于 invokedynamic指令的主要目的。

動(dòng)態(tài)類型語(yǔ)言的概念是: 意思就是類型的檢查是在運(yùn)行時(shí)做的而非編譯期。

而Java本身則是靜態(tài)類型語(yǔ)言, 這一點(diǎn)又在哪里能夠體現(xiàn)呢?

obj.println('language');

如果處在java環(huán)境中,且obj的靜態(tài)語(yǔ)言類型是 java.io.PrintStream, 那么obj本身的實(shí)際類型也必須是PrintStream的子類才行, 哪怕本身存在 println方法也不可以, 但同樣的問(wèn)題放在 javascript中就不同了, 只要實(shí)際類型中存在println方法, 執(zhí)行就不會(huì)有任何問(wèn)題.

這點(diǎn)就是因?yàn)? java在編譯時(shí)已經(jīng)將其完整的符號(hào)引用生成出來(lái), 如果注意到的話, 會(huì)發(fā)現(xiàn)無(wú)論是動(dòng)態(tài)分派還是靜態(tài)分派, 在編譯的指令中都是已經(jīng)精確到相應(yīng)類的某一個(gè)方法中了, 如此, 自然只能夠在有限的范圍內(nèi)略做調(diào)整, 如果超出了當(dāng)前類的范圍, 就無(wú)法調(diào)用了.

jvm虛擬機(jī)并不僅僅是java語(yǔ)言的虛擬機(jī), 那么如何為動(dòng)態(tài)類型語(yǔ)言提供支持就是一個(gè)問(wèn)題了, 并且在目前java8中的lamda表達(dá)式中也應(yīng)用的是 invokedynamic指令.

MethodHandle

而與之相關(guān)的jar包則是 java.lang.invoke, 相關(guān)的類則是 MethodHandle.

在這里我也并不想再談 MethodHandle的使用方法, 網(wǎng)上資料實(shí)在不少.

需要提到的是, 它的功能和java的反射略有相似, 通過(guò)方法名, class, 就可以調(diào)用相應(yīng)的方法. 但它比起反射要輕量級(jí); 且Reflection是在模擬Java代碼的調(diào)用, MethodHandle是在模仿字節(jié)碼層面的調(diào)用.

這個(gè)方法不失為是在動(dòng)態(tài)調(diào)用中除了反射之外的另一種選擇.

基于棧解釋器的執(zhí)行過(guò)程

其實(shí)本文更像是在 前一篇博客中 java內(nèi)存區(qū)域中的虛擬機(jī)棧的一種補(bǔ)充說(shuō)明.

而真實(shí)的執(zhí)行流程, 我想通過(guò)下文的代碼來(lái)看:

public int add() { int a = 100; int b = 200; int c = 300; return (a + b) * c;}-- javap -verbose Mainpublic int add();// 返回類型為 intdescriptor: ()Iflags: ACC_PUBLICCode://需要深度為2的操作數(shù)棧, 4個(gè)slot的局部變量空間, 有一個(gè)參數(shù)為 this stack=2, locals=4, args_size=1 //將100推入操作數(shù)棧頂, 棧:100 0: bipush 100 //將棧頂?shù)臄?shù)據(jù)出棧并存儲(chǔ)到局部變量表的第一個(gè)slot中(從0開(kāi)始) //此時(shí):棧: - 局部變量表: slot1 100 2: istore_1 //與上面類似,重復(fù)過(guò)程 3: sipush 200 6: istore_2 7: sipush 300 //此時(shí):棧: - 局部變量表: slot1 100 slot2 200 slot3 300 10: istore_3 //將局部變量表 slot1的值復(fù)制到 棧頂 11: iload_1 //將局部變量表 slot2的值復(fù)制到 棧頂 此時(shí):棧: 200 100 12: iload_2 //棧頂兩個(gè)元素出棧, 并相加, 結(jié)果重新入棧. 此時(shí): 棧: 300 13: iadd //將局部變量表 slot3的值復(fù)制到 棧頂 此時(shí):棧: 300 300 14: iload_3 //將棧頂元素相乘, 結(jié)果重新入棧 15: imul //將棧頂?shù)慕Y(jié)果返回給方法調(diào)用者. 方法執(zhí)行結(jié)束 16: ireturn LineNumberTable: line 85: 0 line 86: 3 line 87: 7 line 88: 11

基于棧的執(zhí)行引擎正是通過(guò)這樣出棧入棧的方式完成指令, 而基于寄存器的則不然, 是將操作數(shù)存入寄存器, 同時(shí)將輸入值也就是指令參數(shù) 與 某寄存器的存儲(chǔ)值相加. 區(qū)別就在于存儲(chǔ)位置, 以及參數(shù)問(wèn)題, 基于棧的大部分指令都是無(wú)參數(shù)指令, 指令很明確的規(guī)定了 要用哪幾個(gè)棧元素, 棧元素的類型是什么.

我們平常所使用的電腦, 其 X86指令集, 正是基于寄存器的指令集.

優(yōu)缺點(diǎn)則是: 基于棧, 可移植性較強(qiáng), 但速度比較慢, 慢的原因一是需要許多冗余操作, 代碼. 二是基于棧是基于內(nèi)存的操作方式, 而內(nèi)存的速度比起寄存器更是要慢上許多.

總結(jié)

本文大致介紹這樣幾點(diǎn):

java多態(tài)在 jvm層次的實(shí)現(xiàn).

為什么說(shuō)jvm執(zhí)行引擎是基于棧的執(zhí)行引擎, 以及究竟是怎樣一個(gè)流程.

以上就是Java虛擬機(jī)執(zhí)行引擎知識(shí)總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Java虛擬機(jī)執(zhí)行引擎的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 大余县| 广平县| 治县。| 德州市| 宁安市| 四子王旗| 漯河市| 偏关县| 大安市| 宁德市| 会昌县| 乡城县| 丽江市| 鹤峰县| 张家口市| 桐柏县| 日喀则市| 上思县| 沛县| 当雄县| 个旧市| 昌黎县| 高青县| 金寨县| 玛曲县| 建始县| 神农架林区| 沙洋县| 福海县| 绥芬河市| 阿勒泰市| 延庆县| 满城县| 柯坪县| 综艺| 兴业县| 宝鸡市| 龙游县| 外汇| 开平市| 栾城县|