欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2020年06月02日 09:30:41   作者:蝴蝶刀刀  
這篇文章主要介紹了有關(guān)Java虛擬機(jī)執(zhí)行引擎的知識,文中實(shí)例簡單易懂,方便大家更好的學(xué)習(xí),有興趣的朋友可以了解下

執(zhí)行引擎

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

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

1 局部變量表:

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

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

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

2 操作數(shù)棧:

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

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

4 方法返回地址:

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

方法調(diào)用

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

在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)行時動態(tài)解析出調(diào)用點(diǎn)限定符所引用的方法, 然后再執(zhí)行該方法.

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

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

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

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

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

方法分派(dispatch)

1 靜態(tài)分派

對于代碼

 Human man = new Man();

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

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

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

2 動態(tài)分派

動態(tài)分派 與 重寫息息相關(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)在的問題在于, 虛擬機(jī)如何知道, 究竟調(diào)用的是哪個方法?

 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)注幾個方法的執(zhí)行點(diǎn), invokespecial不用多說, 之前提到過, 是執(zhí)行 構(gòu)造器方法時 的指令

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

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

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

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

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

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

動態(tài)類型語言

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

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

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

obj.println("language");

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

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

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

MethodHandle

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

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

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

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

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

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

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

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個slot的局部變量空間, 有一個參數(shù)為 this
 stack=2, locals=4, args_size=1
 //將100推入操作數(shù)棧頂, 棧:100   0: bipush    100
  //將棧頂?shù)臄?shù)據(jù)出棧并存儲到局部變量表的第一個slot中(從0開始)
  //此時:棧: - 局部變量表: slot1 100   2: istore_1   //與上面類似,重復(fù)過程   3: sipush    200   6: istore_2   7: sipush    300
   //此時:棧: - 局部變量表: slot1 100 slot2 200 slot3 300  10: istore_3  //將局部變量表 slot1的值復(fù)制到 棧頂  11: iload_1  //將局部變量表 slot2的值復(fù)制到 棧頂 此時:棧: 200 100  12: iload_2  //棧頂兩個元素出棧, 并相加, 結(jié)果重新入棧. 此時: 棧: 300  13: iadd  //將局部變量表 slot3的值復(fù)制到 棧頂 此時:棧: 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í)行引擎正是通過這樣出棧入棧的方式完成指令, 而基于寄存器的則不然, 是將操作數(shù)存入寄存器, 同時將輸入值也就是指令參數(shù) 與 某寄存器的存儲值相加. 區(qū)別就在于存儲位置, 以及參數(shù)問題, 基于棧的大部分指令都是無參數(shù)指令, 指令很明確的規(guī)定了 要用哪幾個棧元素, 棧元素的類型是什么.

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

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

總結(jié)

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

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

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

以上就是Java虛擬機(jī)執(zhí)行引擎知識總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Java虛擬機(jī)執(zhí)行引擎的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論