Java虛擬機-字節碼指令

字節碼指令

Java虛擬機的指令由一個字節長度的、代表着某種特定操作含義的數字(稱爲操作碼,Opcode)以及跟隨其後的零至多個代表此操作所需參數(稱爲操作數,Operands)構成。Java虛擬機採用面向操作數棧而不是面向寄存器的架構,所以大多數指令都不包含操作數,只有一個操作碼。
Java虛擬機操作碼長度限制爲一個字節(0-255),並且Class文件格式放棄類編譯後代碼的操作數長度對齊。所以虛擬機處理那些超過一個字節數據的時候,不得不在運行時從字節中重建出具體的數據的結構。這種操作會導致解釋執行字節碼時損失一些性能,但是這樣意味着節省很多填充和間隔符號,編譯代碼也更加短小精幹。這也是因爲Java最初設計是面向網絡、智能家電的技術背景,並且一直沿用至今。現在的網絡帶寬相比幾十年前已經擴寬了很多倍,本地的計算性能也提高了無數倍。那現在這個思想是否還需要繼續保持呢?在56k撥號上網、386的時代Java虛擬機針對編譯時的優化或許決定了應用的成敗,但是在現在5G和16甚至32核服務器以及Java虛擬機自身的發展與優化,常見的問題主要就是OOM了。

字節碼與數據類型

Java虛擬機的指令集中,大多數指令都包含了其操作對應的數據類型,記得剛纔說過操作碼長度只有一個字節,意味着操作碼總數不能超過256條。Java虛擬機支持的數據類型包含8種(byte,short,int,long,float,double,char,reference),大部分指令都不支持byte、short、和char,甚至沒有任何指令支持boolean。編譯期會在編譯期或運行期將byte和short的數據帶符號、boolean和char數據零位擴展爲int類型。
指令中包含例如 const、load指令,針對int、long、float、double、reference分別爲:iconst,lconst,fconst,dconst,aconst;iload、lload、fload、dload、aload。

加載和存儲指令

load相關指令將一個局部變量加載到操作棧。
store相關指令將一個數值從操作數棧存儲到局部變量表。
push、const相關指令將一個常量加載到操作數棧。
擴充局部變量表的訪問索引的指令:wide。

運算指令

運算指令用於對兩個操作數棧上的值進行某種特定運算,並把結果重新存入到操作棧頂,大體分爲兩種:整型數據運行指令與浮點型數據進行運算指令。
所有指令包括:加(add),減(sub),乘(mul),除(div),求餘(rem),取反(neg),位移(shl,shr),按位或(or),按位與(and),按位異或(xor),局部變量自增(inc),比較(cmp)

類型轉換指令

無需顯式轉換,寬化類型轉換(小範圍向大範圍的安全轉換):int到long、float、double;long到float、double;float到double;
需要顯示轉換,窄化類型轉換:int到byte、char、short;long到int;float到int、long;double到int、long、float;
Java虛擬機規範中明確規定數值類型的窄化轉換指令永遠不可能導致虛擬機拋出運行時異常。

對象創建與訪問指令

創建類實例的指令:new
訪問類字段:getfield、putfield、getstatic、putstatic
檢查類實例類型:instanceof、checkcast

創建數組:newarray、anewarray、multianewarray
加載數組元素到操作數棧:aload相關
將操作數棧的值存儲到數組元素中的指令:astore
取數據長度:arraylength

操作數棧管理指令

將操作數棧頂一個或兩個元素出棧:pop,pop2
複製棧頂元素的數值並且壓入棧頂:dup、dup_x等相關
棧頂兩個數值互換:swap

控制轉移指令

控制轉移指令可以讓Java虛擬機有條件或者無條件從指定位置指令而不是下一條指令繼續執行程序。從概念模型上理解,可以認爲控制轉移指令是在有條件或無條件的修改PC寄存器的值。
條件分支:if相關(ifeq、iflt、ifnotnull等)
複合條件分支:switch(tableswitch,lookupswitch)
無條件分支:goto,jsr(goto、goto_w、jsr、jsr_w、ret)
由於各種數據類型的比較最終都會轉換爲int進行比較,所以int類型的條件分支指令時最爲豐富和強大的。

方法調用和返回指令

invokevirtual用於調用對象的實例方法,最常見的用法。
invokeinterface用於調用接口方法。
invokespecial用於調用特殊處理的實例方法,如實例初始化方法、私有方法、父類方法。
invokestatic用於調用靜態方法。
invokedynamic用於在運行時動態解析出調用點限定符所引用的方法,並執行該方法。
方法調用指令與數據類型無關,返回指令與數據類型有關。
指令包括:ireturn(返回值時boolean、byte、char、short、int時使用)、lreturn、freturn、dreturn、areturn、return(返回值void,實例初始化方法、類和接口的類初始化方法使用)。

異常處理指令

異常拋出都有athrow指令實現。
處理異常(catch語句)不是有字節碼指令來實現的(很久之前曾經使用jsr和ret指令,現在已經不用了),而是採用異常表來完成的。

同步指令

Java中的同步關鍵字爲synchronized語句塊,指令集中有monitorenter和monitorexit兩條指令支持。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章