JVM(四):深入分析Java位元組碼-下

iceWang丶發表於2019-06-11

JVM(四):深入分析Java位元組碼-下

在上文中,我們講解了 Class 檔案中的檔案標識,常量池等內容。在本文中,我們就詳細說一下剩下的指令集內容,闡述其分別代表了什麼含義,以及 JVM 團隊這樣設計的意義。

簡介

JVM 指令設計為僅有一個位元組長度,由操作碼和緊隨其後的零至多個運算元來構成。

這裡說到 JVM 的指令僅有一個位元組,這意味著 JVM 在操作超過一個位元組長度的資料時,需要在執行時重建出多位元組資料型別的具體資料結構,例如 Long 等。這會導致這個操作不是原子操作,在高併發的情況下,就有可能會導致錯誤。

由於 JVM 的操作碼長度只有一個位元組,因此設計指令的時候,需要考慮所有指令加起來不能超過一個位元組長度,正因如此,有許多資料型別是沒有其對應的操作碼的,其操作的方式是將其資料型別進行向上轉型為其他的資料型別來參與運算。

例如大多數對於 boolean、byte、short 和 char 型別資料的操作,實際上是將其轉換成 int 型別來處理的。

指令詳解

JVM 指令如果詳細來說的話有一百多個,在這裡全部展開來描述的,不免有流水賬的嫌疑,且價值不大,因此在本文中僅粗略描述一下,並找了一些關鍵的指令對其進行詳細拆解,如果讀者對其他指令有興趣的話可以自行 Google 或翻書學習。
全部指令的內容

  • 載入和儲存指令:用於將資料在棧幀中的區域性變數表和運算元棧之間轉移(棧幀的佈局放在以後的文章 JVM-記憶體佈局中進行介紹,在這裡讀者只要明白其是根據棧進行操作就可以了)。eg:load,store;

  • 運算指令:對兩個運算元棧上的值進行計算並重新存入到操作棧頂。eg:add,sub,mul,div,rem,neg,shr,or,and,inc……;

  • 型別轉換指令:將一個值資料型別進行轉換為其他的型別。eg:x2x;

  • 物件建立與訪問指令:new,newarray(陣列和類例項建立和操作是不同的);

  • 運算元棧操作指令:直接操作運算元棧。eg:pop,swap;

  • 控制轉移指令:有條件或無條件的控制 JVM 從指定的位置執行程式。(可以簡單理解為修改程式計數器中的值)。eg:if,goto……;

  • 方法呼叫和返回指令:根據物件的實際型別進行虛方法分配,呼叫類方法,呼叫介面方法等。另外還有根據不同的返回型別的不同返回指令;

  • 異常處理指令:目前異常處理在 JVM 內部是通過異常表來完成的;

    Exception table:
         from    to  target type
             0     8    14   Class java/lang/RuntimeException
             0     8    29   any
            14    23    29   any

from 行 到 to 行之間的位元組碼指令如果出現了 type 以及其子類的型別錯誤,就跳轉到 target 行對應的位元組碼指令進行執行;

  • 同步指令:同步指令是通過管程(Monitor)來實現的。
    • 同步方法內部分為方法級的同步和方法內部一段指令的同步。
    • 方法內部的指令,其實現邏輯是設定方法的訪問標誌:ACC_SYNCHRONIZED,如果其被設定了,表明該同步方法已經被別人呼叫,其他物件無法獲得管程,就需要等待,在獲得管程後才能繼續執行。
    • 指令內部的同步,其實現邏輯是通過位元組碼指令來控制的,位元組碼執行到需要同步的指令時,其會呼叫monitorenter 指令進行同步,此時其他執行緒無法進入這段指令序列,當程式正常或異常退出後,呼叫monitorexit 指令進行鎖釋放,此時其他執行緒就可以執行同步方法了。

PS:上面這段是 synchronized 關鍵字的本質含義,其具體的細節放到高併發程式設計系列文章中詳細來說。

總結

通過 Class 檔案這個中間檔案,JVM 達成了語言無關性和平臺無關性兩個大突破,使得 Java 語言不僅達到了“一次編寫,處處執行”,也使得其他語言只要符合 JVM 規範,就可以像 Java 一樣,達到超然物外的無關性。

iceWang公眾號

文章在公眾號 “iceWang" 第一手更新,有興趣的朋友可以關注公眾號,第一時間看到筆者分享的各項知識點,謝謝!筆芯!

本系列文章主要借鑑自《深入分析 JavaWeb 技術內幕》和《深入理解 Java 虛擬機器- JVM 高階特性與最佳實踐》。

相關文章