虛擬機字節(jié)碼引擎

代碼編譯的結(jié)果就是從本地機器碼變?yōu)樽止?jié)碼

  • 執(zhí)行引擎在執(zhí)行java代碼可以解釋執(zhí)行(通過解釋器執(zhí)行)也可以編譯執(zhí)行(通過即時編譯器產(chǎn)生本地代碼執(zhí)行)

運行時的棧幀結(jié)構(gòu)

  • 棧幀支持方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu),
  • 棧幀存儲了方法的局部變量表(參數(shù)),操作數(shù)棧(對應著代碼的操作),動態(tài)鏈接和方法返回地址等信息
  • 每一個方法從調(diào)用開始到執(zhí)行完成的過程,就對應著一個棧幀在虛擬機棧里面從入棧到出棧的過程。
  • 對于執(zhí)行引擎來說,活動線程中,只有棧頂?shù)臈怯行У?,稱為當前棧幀,這個棧幀所關(guān)聯(lián)的方法稱為當前方法。執(zhí)行引擎所運行的所有字節(jié)碼指令都只針對當前棧幀進行操作。
  • 編譯器局部變量表大小和操作數(shù)棧的深度都已經(jīng)確定,因此一個棧需要多大內(nèi)存也已經(jīng)確定
局部變量表
  • 用于存放方法參數(shù)和方法內(nèi)部定義的局部變量。

  • 出現(xiàn)在方法表中的code屬性的max_locals數(shù)據(jù)項中確定了該方法所需要分配的最大局部變量表的容量。

  • 以slot 為最小單位,slot 沒有說明他的長度 但是起碼可以接受八中類型(基本類型和引用以及returnAddress)

  • slot可以重用,只要方法定義的變量超出作用域既可以覆蓋,這樣做不僅僅節(jié)省棧空間,還可以方便對象回收,因為對象超出作用域,但是如果slot沒被回收,那么對象還是無法被回收,這是因為slot還存在對對象的引用。(局部變量表中的slot 也屬于GC roots)

操作數(shù)棧
  • 對應方法表中的code屬性的max_stacks
  • 操作數(shù)棧的每一個元素可以是任意的java數(shù)據(jù)類型
  • 方法開始執(zhí)行時候操作數(shù)棧是空的,方法執(zhí)行過程對應的像操作數(shù)棧寫入和提取內(nèi)容。例如算術(shù)運算,或者方法內(nèi)部調(diào)用其他方法時候是通過操作數(shù)棧傳遞參數(shù)
  • jvm會做優(yōu)化讓兩個棧幀重疊異步,讓下面的棧幀(主動調(diào)用方法)的部分操作數(shù)棧與上面棧幀(被動調(diào)用方法)的局部變量表重疊在一起。(棧中在下面的方法是主動調(diào)用上面的方法,這樣就可以吧操作數(shù)棧中的數(shù)據(jù)傳遞給上面方法的局部變量表做參數(shù))避免了棧幀之間的拷貝
  • 比如我們要在一個方法中調(diào)用某個方法,那么我們的方法需要的參數(shù)必然已經(jīng)存放在操作數(shù)棧幀中
動態(tài)鏈接
  • 每個棧幀都包含一個指向運行時常量池中該棧幀所屬哪個方法的引用。
  • 持有該引用是為了支持方法調(diào)用過程中的動態(tài)鏈接
  • 我們的符號引用會在類加載和方法運行期間解析成直接引用這就是動態(tài)鏈接
方法返回地址
  • 方法被執(zhí)行退出條件有兩種:執(zhí)行遇到了方法返回的字節(jié)碼指令,或者遇到異常且該異常沒有在方法中處理
  • 正常退出的時候調(diào)用者的pc計數(shù)器可以作為返回地址,棧幀中可能保存這個計數(shù)器值
  • 異常退出的時候,返回地址是通過異常表來確定的,棧幀一般不會保存這部分信息
  • 棧幀退出的操作,恢復上層方法的局部變量表和操作數(shù)棧,把返回值亞茹調(diào)用者棧幀的操作數(shù)棧,調(diào)整pc計數(shù)器的值

方法調(diào)用

  • 方法調(diào)用并不等同于方法執(zhí)行,方法調(diào)用階段唯一的任務就是確定被調(diào)用方法的版本(即調(diào)用那個一個方法)
  • class文件編譯不包含傳統(tǒng)編譯中的連接這一步驟,一切方法調(diào)用在class文件里面存儲的都是符號引用,而不是方法在實際運行時候內(nèi)存布局的入口地址(即直接引用)
解析
  • 在類加載就能確定調(diào)用那個方法的符號引用會被直接解析
  • 符合在編譯器可知,運行不可變的就是靜態(tài)方法和私有方法,前者與類型有關(guān)系,后者不可以被繼承或者重寫,因為在編譯器 我們調(diào)用一個類的方法我們不知道具體的調(diào)用是是否子類的方法。
  • invokestatic,調(diào)用靜態(tài)方法
  • invoekspecial 調(diào)用實例構(gòu)造器方法(構(gòu)造器方法不是構(gòu)造函數(shù) 該方法會執(zhí)行實例變量和代碼塊),私有方法和父類方法
  • invokevirtual 調(diào)用所有的虛方法
  • invokeinterface 調(diào)用接口方法,會在運行時候在確定一個實習此接口的對象
  • 只要是invokestatic,invoekspecial 指令調(diào)用的方法都是可以在類加載階段解析為直接引用,這些方法都可以稱之為非虛方法,其他方法除了 final 都可以成為虛方法
  • final方法雖然是通過invokevirtual指令調(diào)用,但是其只有一個版本 所以是非虛的
分派
  • 分派調(diào)用有可能是靜態(tài)的也可能是動態(tài)的
  • 分派與java 多態(tài)有關(guān)
靜態(tài)分派(重載)
  • 如果我們有類Class其有重載的時候有三個方法 參數(shù)分別是父類A,子類B 子類C,那么我們傳遞一個A a=new B(); 給這個方法 他會調(diào)用參數(shù)是父類A
  • 即靜態(tài)分派只會關(guān)注形參,我們的A是靜態(tài)類型,B是實際類型,jvm在編譯器就把A參數(shù)版本的符號引用寫到指令中
  • 重載并不是固定的 只是選擇最合適的 當我們的參數(shù)變化時候,參數(shù)類型匹配會進行不斷的選擇
動態(tài)分派(重寫)
  • 如果我們有父類A,子類B 子類C,執(zhí)行代碼A a1=new B(); A a2=new C();那我們分別調(diào)用a1.方法和a2的方法 這個方法都在子類重寫了,那么其會調(diào)用b,c重寫的方法
虛擬機實現(xiàn)動態(tài)分配的原理
  • 為類在方法區(qū)建立一個虛方法表(vtable),與此對應的是我們調(diào)用接口的時候也會在字節(jié)碼指令invokeinterface執(zhí)行時用到接口方法表(itable)
    經(jīng)典的圖如下:


    微信截圖_20181123132331.png
  • 上圖 如果某個方法在子類中沒有重寫了,那么子類該方法的地址入口和父類的一致,如果重新了,則子類的方法入口地址會被更改
  • 上圖中son重寫了father的所有方法,所以其方法都知曉子類的實例
  • 但是他們都沒有重寫object的方法 所以object方法都指向object實例
  • 為了程序?qū)崿F(xiàn)上的方便,具有相同簽名的方法在父類和子類的虛方法表中都具有意義的索引號,這樣類型變化的時候僅僅需要更換查找的方法表
  • 虛擬機還有可能使用內(nèi)聯(lián)緩存和基于繼承關(guān)系分析技術(shù)的守護內(nèi)聯(lián)進行優(yōu)化

基于棧的字節(jié)碼解釋執(zhí)行引擎

  • 虛擬機執(zhí)行代碼有兩種方式,解釋執(zhí)行或者編譯執(zhí)行

解釋執(zhí)行

  • 當出現(xiàn)及時編譯器之后,class文件到底是會被解釋執(zhí)行還是編譯執(zhí)行。
    下圖是兩種執(zhí)行方式的圖


    編譯過程.png
  • 指令流基本上是基于棧的指令集架構(gòu)。指令流里面大部分都是零地址指令,他們一來操作數(shù)進棧進行工作

  • 基于寄存器的指令集與基于棧的指令集剛好相對

  • 計算1+1,基于棧的指令集回收iconst_1(把兩個常量壓入棧),iconst_1,iadd,(把棧頂?shù)膬蓚€值出棧相加)istore_0(把棧頂?shù)闹捣诺骄植孔兞勘淼牡?個slot)

  • 基于寄存器的指令集就是 move eax, 1 add eax, 1.move指令把eax設(shè)置為1 ,add再給這個指令加1

  • 基于棧的優(yōu)點就是可移植性但是速度慢,基于寄存器的容易被硬件約束 但是速度快

  • bipush_100 將單字節(jié)的整形常量(100)推入操作數(shù)棧頂

  • istore_1 將操作數(shù)棧頂?shù)臄?shù)據(jù)存到第一個局部變量slot

  • iload_1 從局部變量表中的第一個slot獲取數(shù)據(jù)壓入棧頂

  • iadd把操作數(shù)棧頂?shù)?位數(shù)彈出相加

  • ireturn把操作數(shù)棧頂返回值返回給此方法的調(diào)用者


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

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

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