深入理解JVM(六)——類文件結構——code -开发者知识库

深入理解JVM(六)——類文件結構——code -开发者知识库,第1张

Code

Java程序中方法體中的代碼經過Javac編譯器處理之后,最終變成字節碼指令存儲在Code屬性內。

Code屬性出現在方法表的屬性集合之中,但不是所有的方法表都必須有,譬如接口或者抽象類。

Code是Class文件中最重要的一個屬性,如果把一個Java程序中的信息分為代碼(Code,方法體里面的Java代碼)和元數據(MetaData,包括類,字段,方法定義以及其它信息)兩部分

類型 名稱 數量 說明
u2 attribute_name_index 1 指向constant_Utf8_info型常量的索引,常量值固定位“Code”,代表該屬性的屬性名稱
u4 attribute_length 1 屬性值的長度
u2 max_stack 1 操作數棧深度的最大值,方法執行的任何時刻不會超過這個深度,JVM根據這個值分配棧幀中操作數的深度
u2 max_locals 1 局部變量表所需的存儲空間,單位Slot,4個字節,不超過32位的數據類型,每個局部變量占1個Slot,64位的用兩個Slot存放。包括this,異常處理參數,方法簽名參數等。Slot可以重用(超過局部變量的作用域)。
u4 code_length 1 字節碼的長度
u1 code code_length 字節碼指令的一系列字節流,指令的意思及參數,u1類型的長度,最多有256條指令
u2 exception_table_length 1 顯示異常處理表集合,見下面的說明
exception_info exception_table exception_table_length 1
u2 attribute_count 1 1
attribute_info attributes attribute_count 1

Java虛擬機執行字節碼是基於棧的體系結構

Javac編譯器編譯的時候會把this關鍵字的訪問轉變成一個普通方法參數的訪問,只是虛擬機調用實例方法時自動傳入參數而已。因此局部變量表至少會有一個指向當前對象實例的局部變量。

異常表實際上是Java代碼的一部分,編譯器使用異常表而不是簡單的跳轉命令(jsr,ret已經不用了)來實現java異常及finally處理機制。
異常處理表:有4個字段,因為較好理解所以不畫表格了start_pc,end_pc,catch_pc,handler_pc
如果代碼在第start_pc行到第end_pc行之間(不包括end_pc),出現了類型為catch_type或者其子類的異常(catch_type指向一個constant_class_info型常量的索引),則轉到第handler_pc行繼續處理。當catch_type的值為0時,代表任意異常情況都需要轉向handler_pc處進行處理。

字節碼指令介紹

Java虛擬機的指令由一個字節長度的,代表着某種特定操作含義的數字(操作碼)以及跟隨其后的零至多個代表此操作所需參數(操作數)而構成。

由於Java虛擬機采用面向操作數棧而非寄存器的架構,所以大多數的指令都不包括操作數,而只有一個操作碼。

Java虛擬機的指令集中,大多數的指令都包含了其操作對應的數據類型信息。如iload指令用於從局部變量表中加載int型的數據到操作數棧中,fload則是加載float型的數據。

i代表int類型
l代表long
s代表short
b代表byte
c代表char
f代表float
d代表double
a代表reference
但是之前說過指令集的數量只有一個字節,編譯器會在編譯期或者運行期將byte和short類型的數據帶符號擴展為相應的int類型數據,將boolean和char類型零位擴展為相應的int型數據類型。

加載和存儲指令

將一個局部變量加載到操作棧:
iload,iload_< n>,
lload,lload_< n>,
fload,fload_< n>,
dload,dload_< n>,
aload,aload_< n>

將一個數值從操作數棧存儲到局部變量表
istore,istore_< n>,
lstore,lstore_< n>,
fstore,fstore_< n>,
dstore.dstore_< n>,
astore,astore_< n>

將一個常量加載到操作數棧
bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_m1,
iconst_< i>,lconst_< i>,fconst_< l>,dconst_< d>

擴充局部變量表的訪問索引的指令:wide

運算符指令

加法指令:iadd,ladd,fadd,dadd
減法指令:isub,lsub,fsub,dsub
乘法指令:imul,lmul,fmul,dmul
除法指令:idiv,ldiv,fdiv,ddiv
求余指令:irem,lrem,frem,drem
取反指令:ineg,leng,fneg,dneg
位移指令:ishl,ishr,iushr,lshl,lshr,lushr
按位或指令:ior,lor
按位與指令:iand,land
按位異或指令:ixor,lxor
局部變量自增指令:iinc
比較指令:dcmpg,dcmpl,fcmpg,fcmpl,lcmp

類型轉化指令

虛擬機在處理窄化類型轉換時,必須顯示地使用轉化指令
i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f

對象創建與訪問指令

創建類實例的指令 new
創建數組的指令 newarray anewarray multitianewarray
訪問類字段(static字段,或稱為類變量),實例字段的指令:
getfield putfield getstatic putstatic
把一個數組元素加載到操作數棧的指令:baload caload saload iaload laload faload daload aalaod
將一個操作數棧的值存儲到數值元素的指令:bastore castore sastore iastore lastore fastore dastore aastore
取數組的長度的指令:arraylength
檢查類實例類型的指令 instanceof checkcast

操作數棧轉移指令

出棧一個或兩個元素 pop pop2
復制棧頂一個或兩個元素並將復制值重新壓入棧dup dup2 dup_x1 dup2_x1 dup_x2 dup2_x2
棧頂元素互換 swap

控制轉移指令

修改寄存器的值的指令
條件分支 ifeq iflt ifle ifne ifgt ifge ifnull ifnonnull …
復合條件分支 tableswitch lookupswitch
無條件分支 goto goto_w jsr jsr_w ret

方法調用和放回指令

invokevirtual 指令調用對象的實例方法
invokeinterface 調用接口的方法
invokespecial 調用一些特殊處理的實例方法(初始化,私有方法,父方法)
invokestatic 調用類方法(static)
invokedynamic 用於運行時動態解析出調用點限定符所引用的方法,並執行該方法

異常處理指令

現在Java虛擬機中用異常處理表來完成

同步指令

monitorenter
monitorexit
通常用來支持Java語言中的synchronized關鍵字的語義

最佳答案:

本文经用户投稿或网站收集转载,如有侵权请联系本站。

发表评论

0条回复