Java的Class类文件结构及基本字节码指令
Class類文件的結(jié)構(gòu)
概念:Class文件是一組以8位字節(jié)為基礎(chǔ)單位的二進(jìn)制流
按順序整齊排列
沒有任何分隔符,內(nèi)容全部是運(yùn)行時(shí)的必要數(shù)據(jù),沒有空隙存在
-
排序方式:高位在前
Big-Endian:最高字節(jié)在地址最低位,最低字節(jié)在地址最高位
Little-Endian:相反 -
儲(chǔ)存方式:類似于C語言結(jié)構(gòu)體的偽結(jié)構(gòu)來儲(chǔ)存只有兩種數(shù)據(jù)類型
無符號(hào):屬于基本數(shù)據(jù)類型
u1:1個(gè)字節(jié)
u2:2個(gè)字節(jié)
u4:4個(gè)字節(jié)
u8:8個(gè)字節(jié) -
表:多個(gè)無符號(hào)數(shù)或其他表作為數(shù)據(jù)項(xiàng)構(gòu)成的復(fù)合數(shù)據(jù)類型
以"_info"結(jié)尾
描述有層次關(guān)系的復(fù)合結(jié)構(gòu)數(shù)據(jù)
整個(gè)Class文件本質(zhì)上就是一張表 -
魔數(shù): 魔數(shù)
每個(gè)Class文件的頭4個(gè)字節(jié)
確定這個(gè)文件是不是一個(gè)能被虛擬機(jī)接收的Class文件
Java中的值為:0xCAFEBABE->咖啡寶貝 -
Class文件版本:
緊接著的4個(gè)字節(jié)
第5個(gè)和第6個(gè):次版本號(hào)(Minor Version)
第7個(gè)和第8個(gè):主版本號(hào)(Major Version)
滿足向下兼容,即高版本兼容低版本的Class文件,反之不行 -
常量池:
緊接著版本號(hào)
Class文件中的資源倉(cāng)庫,占用空間最大
常量池容量計(jì)數(shù)值(constant_pool_count),從1開始而不是從0開始計(jì)數(shù)
常量池中常量數(shù)量不確定,需要一個(gè)計(jì)數(shù)是u2類型的數(shù)據(jù)
設(shè)計(jì)者將從0位做特殊考慮,特殊情況下,有些指向常量池的索引數(shù)據(jù)要表達(dá)"不引用任何常量池項(xiàng)目"的意義
常量池容量(偏移地址:0x00000008),十進(jìn)制22,表示常量池中有21項(xiàng)常量,索引1~21
主要存放數(shù)據(jù)類型字面量(Literal):
如文本字符串,final常量等符號(hào)引用(Symbolic References)
類和接口的全限定名
字段的名稱和描述符
方法的名稱和描述符 -
14種常量池的每個(gè)常量表:
constant_utf8_info 1 utf-8
u1 tag 1 標(biāo)志位//用于區(qū)分常量類型
u2 length 1 utf-8真正的字符串內(nèi)容
constant_integer_info 3 整型
constant_float_info 4 浮點(diǎn)型
constant_long_info 5 長(zhǎng)整型
constant_double_info 6 雙精度
constant_class_info 7 類或接口的符號(hào)引用
constant_string_info 8 字符串型
constant_fieldref_info 9 字段符號(hào)引用
constant_methodref_info 10 類中方法的符號(hào)引用
constant_interfaceMethodref_info 11 接口中符號(hào)引用
constant_NameAndType_info 12 字段或方法的部分符號(hào)引用
constant_MethodHandle_info 15 方法句柄
constant_MethodType_info 16 表示方法類型
constant_InvokeDynamic_info 18 動(dòng)態(tài)方法調(diào)用點(diǎn) -
訪問標(biāo)識(shí):緊接著常量池,兩個(gè)字節(jié),總共16位,當(dāng)前只定義了其中8個(gè),未使用一律使用0
ACC_PUBLIC 0x0001 public
ACC_FINAL 0x0010 final
ACC_SUPER 0x0020 invokespecial
ACC_INTERFACE 0x0400 abstract
ACC_SYNTHETIC 0x1000 并非由用戶代碼產(chǎn)生
ACC_ANNOTATION 0x2000 注解
ACC_ENUM 0x4000 枚舉 -
類索引(this_class),父類索引(super_class)與接口索引(interfaces)集合(u2類型數(shù)據(jù))
緊接著訪問標(biāo)志
u2類型(this_class,super_class)u2類型集合(interfaces)
各索引的含義:
這三項(xiàng)數(shù)據(jù)來確定類的繼承關(guān)系
類索引(this_class)用來確定類全限定名
父類索引(super_class)確定父類的全限定名
接口索引描述類實(shí)現(xiàn)的接口,按從左到右的順序排列
父類索引(super_class)除了java.lang.Object外,其他類都不為0 -
字段集合(field_info)
描述接口/類中聲明的變量包含:
作用域(public private protected)
實(shí)例變量還是類變量(static)
可變性(final)
并發(fā)可見性(volatile)
序列化(transient)
字段數(shù)據(jù)類型
字段名稱
全限定名,相當(dāng)于類的全路徑,只是把.換成/,如java/lang/Object
簡(jiǎn)單名稱,方法或?qū)傩悦暮?jiǎn)寫,如inc()方法和m屬性,則簡(jiǎn)單名稱為inc和m
描述符:
描述字段數(shù)據(jù)類型,方法的參數(shù)列表和返回值
基本數(shù)據(jù)類型和void都用一個(gè)大寫字母來表示,對(duì)象類型用L加全限定名來表示
標(biāo)識(shí)名: B(byte), C(char), D(double), F(float), I(int), J(long), S(short), Z(boolean), V(void), L(對(duì)象類型)
數(shù)組描述符:
一維數(shù)組:String[]–>[Ljava/lang/String
二維數(shù)組:String[][]–>[[Ljava/lang/String
方法描述符:
參數(shù)列表在前,返回值在后的順序參數(shù)列表放在()內(nèi),如:void inc()描述為()V
int indexOf(char[] source,int sourceOffset, int sourceCount,char[] target, int targetOffset, int targetCount,int fromIndex)描述為 ([CII[CII)I -
方法表集合:
包含:
訪問標(biāo)志(access_flags)
名稱索引(name_index)
描述符索引(descriptor_index)
屬性表集合(attributes)
方法表集合中沒有方法里面的代碼,Java代碼存在屬性表中名為"Code"的屬性里面 -
重寫和重載的原理:
Override:如果子類未重寫父類方法,方法表集合中就不會(huì)出現(xiàn)來自父類的方法信息,但是有可能出現(xiàn)編譯器自動(dòng)添加方法,如構(gòu)造方法
OverLoad:重載方法,保證和原方法具有相同的簡(jiǎn)單名稱之外,還必須擁有一個(gè)與原方法不同的特征簽名,返回值沒有包含在特征簽名中,因此無法依靠返回值確定方法重載,在Class文件中,如果一個(gè)方法具有相同的名稱和特征簽名,但返回值不同,可以包含在同一個(gè)Class文件中
特征簽名:一個(gè)方法中各個(gè)參數(shù)在常量池中的字段符號(hào)引用的集合 -
屬性表集合:
描述某些場(chǎng)景的專有信息, 沒有太多順序長(zhǎng)度內(nèi)容的限制
虛擬機(jī)規(guī)范預(yù)定義的屬性
屬性名及使用位置及含義:
Code 方法表 Java代碼編譯成的字節(jié)碼文件
ConstantValue 字段表 final關(guān)鍵字定義的常量
Deprecated 類方法表字段表 被聲明為deprecated的方法和字段
Exceptions 方法表 方法拋出異常
EnclosingMethod 類文件 一個(gè)類為匿名類,局部類擁有此屬性,用于標(biāo)識(shí)這個(gè)類所在的外圍方法
InnerClass 類文件 內(nèi)部類列表
LineNumber Code屬性 Java源代碼的行號(hào)與字節(jié)碼指令的對(duì)應(yīng)關(guān)系
LocalVariableTable Code屬性 方法的局部變量描述
StackMapTable Code屬性 JDK1.6新增,類型檢查,處理目標(biāo)方法的局部變量和操作數(shù)棧所需的類型是否匹配
Signature 類方法表字段表 JDK1.5新增,支持泛型情況下方法簽名
SourceFile 類文件 記錄源文件名
SourceDebugExtension 類文件 JDK1.6增,用于存儲(chǔ)額外的調(diào)試信息
Synthetic 類方法表字段表 標(biāo)識(shí)方法或字段為編譯器自動(dòng)生成的
LocalVariableTypeTable JDK1.5新增,使用特征簽名代替描述符
RuntimeVisibleAnnotations 類方法表字段表 JDK1.5新增,為動(dòng)態(tài)注解提供支持,指明哪些注解是運(yùn)行可見
BootstrapMethods JDK1.7新增,用于保存invokedynamic指令引用的引導(dǎo)方法限定符
Code屬性:
Java編譯器處理后的字節(jié)碼指令存儲(chǔ)在Code屬性內(nèi); Code屬性出現(xiàn)在方法表的屬性集合中; 并非所有的方法都必須存在這個(gè)屬性
類型 名稱 數(shù)量
u2 attribute_name_index(指向CONSTANT_UF8_info類型常量的索引) 1
u4 attribute_length(屬性值長(zhǎng)度) 1
u2 max_stack(操作數(shù)占最大深度,在執(zhí)行操作數(shù)棧時(shí)都不會(huì)超過這個(gè)最大深度,JVM在需要的時(shí)候根據(jù)此值分配棧幀中的操作棧深度) 1
u2 max_locals(局部變量表所需的儲(chǔ)存空間根據(jù)Slot//局部變量最小單位,計(jì)算) 1
u4 code_length(編譯后的字節(jié)碼指令) 1
u1 code(編譯后的字節(jié)碼指令流) code_length
u2 exception_table_length(異常表長(zhǎng)度) 1
exception_info exception_table(異常表內(nèi)容) exception_table_length
u2 attributes_counts(屬性數(shù)量) 1
attribute_info attributes(屬性內(nèi)容) attributes_counts
Code屬性是Class文件中最重要的一個(gè)屬性,如果把Class文件分解為Code和Metadata,Code可以描述為代碼,Metadata解釋其他數(shù)據(jù)
attribute_name_index:固定為"Code",表示該屬性的名字
attribute_length:由于屬性名和屬性長(zhǎng)度一共6個(gè)字節(jié)(u2+u4),所以屬性值的長(zhǎng)度就是整個(gè)屬性表的長(zhǎng)度減去這兩個(gè)屬性
max_stack:在方法執(zhí)行的任意時(shí)刻,操作數(shù)棧都不會(huì)超過這個(gè)長(zhǎng)度,虛擬機(jī)運(yùn)行時(shí)根據(jù)這個(gè)值分配棧幀中的操作棧深度
max_locals:單位Solt,是虛擬機(jī)為局部變量分配內(nèi)存的最小單位; 注:Slot重用,并不是方法中使用多少局部變量,就把這些局部變量所占的Slot之和作為max_locals.當(dāng)代碼執(zhí)行超出一個(gè)局部變量的作用域時(shí),這個(gè)局部變量所占的Slot可以被其他局部變量所使用
code_length:code_length代表編譯后的字節(jié)碼長(zhǎng)度,code是儲(chǔ)存字節(jié)碼指令的一系列字節(jié)流
exception_table_length:不是必須存在的 -
this關(guān)鍵字:
任何實(shí)例方法中都能通過this訪問所屬對(duì)象
javac編譯器編譯的時(shí)候把對(duì)this關(guān)鍵字訪問轉(zhuǎn)變?yōu)橐粋€(gè)對(duì)普通方法參數(shù)的訪問然后在虛擬機(jī)調(diào)用實(shí)例方法時(shí)自動(dòng)傳入此參數(shù),所以在實(shí)例方法的局部變量表中至少會(huì)存在一個(gè)指向當(dāng)前對(duì)象的實(shí)例局部變量
局部變量表中也會(huì)預(yù)留出第一個(gè)Slot位來存放對(duì)象實(shí)例的引用,方法參數(shù)值從1開始計(jì)算,這個(gè)處理只會(huì)對(duì)實(shí)例方法有效,對(duì)靜態(tài)方法無效 -
Exceptions屬性(下列方法將會(huì)拋出受查異常,方法描述時(shí)在throws關(guān)鍵字后面列舉的異常)
u2 attribute_name_index 1
u4 attribute_length 1
u2 number_of_exceptions 1
u2 exception_index_table number_of_exceptions -
LineNumberTable屬性
描述Java源碼行號(hào)與字節(jié)碼行號(hào)(字節(jié)碼的偏移量)之間的對(duì)應(yīng)關(guān)系不是運(yùn)行時(shí)必須屬性,但會(huì)默認(rèn)生成到Class文件中
可以在Javac中使用-g:none/lines來取消生成這項(xiàng)信息不生成該屬性,拋出異常時(shí),堆棧中部會(huì)顯示出錯(cuò)的行號(hào),并且在調(diào)試的時(shí)候,也無法按照源碼來設(shè)置斷點(diǎn) -
LocalVariableTable屬性
描述棧幀中局部變量表中變量與Java源碼中定義的變量之間的關(guān)系; 不是運(yùn)行時(shí)必須屬性,但會(huì)默認(rèn)生成到Class文件中; 可以在Javac中使用-g:none/vars來取消或者要求生成該屬性或不生成該屬性; 當(dāng)其他人引入該方法時(shí),所有的參數(shù)名稱都會(huì)丟失,IDE將會(huì)使用arg0,arg1之類的占位符代替原有的參數(shù)名,但對(duì)程序運(yùn)行沒有影響 -
SourceFile屬性:
記錄生成Class文件的源碼文件名稱
可選的屬性
可以在Javac中使用-g:none/source來關(guān)閉/生成這項(xiàng)信息
不開啟,內(nèi)部類拋出異常的時(shí)候,堆棧不會(huì)顯示出錯(cuò)代碼所屬文件名 -
ConstantValue屬性:
通知虛擬機(jī)自動(dòng)為靜態(tài)變量賦值
如果是非靜態(tài)變量,賦值在方法中
靜態(tài):被final static修飾,并且是基本類型/String,就要生成ConstantValue來初始化; 否則一致在方法中初始化 -
InnerClass屬性:
記錄內(nèi)部類與宿主類之間的關(guān)聯(lián);如果定義內(nèi)部類,編譯器會(huì)為他及內(nèi)部類生成InnerClass屬性
Synthetic屬性:表示字段或方法不是由Java源代碼直接生成的,而是由編譯器添加
其他屬性不做介紹.很少使用
JVM字節(jié)碼指令
一個(gè)字節(jié)長(zhǎng)度,代表某種特定操作含義的數(shù)字(操作碼Opcode)以及后面的0至多個(gè)代表此操作所需參數(shù)(操作數(shù)Operands)構(gòu)成
- JVM面向操作數(shù)棧,而非寄存器:
優(yōu)勢(shì):放棄操作數(shù)長(zhǎng)度對(duì)齊,省略很多空格換行符
缺點(diǎn):導(dǎo)致解釋執(zhí)行字節(jié)碼損失性能 - 字節(jié)碼與數(shù)據(jù)類型:
i-int,l-long,s-short,b-byte,c-char,f-float,d-double,a-reference
加載儲(chǔ)存指令(用于將數(shù)據(jù)在棧幀中的局部變量表和操作數(shù)棧之間來回傳輸):
將一個(gè)局部變量加載到操作棧: iload,iload_,…其他以此類推
將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量表: istore,istore_,…其他以此類推
將一個(gè)常量加載到操作數(shù) - 棧: bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,lconst_,fconst_,dconst_
擴(kuò)充局部變量表的訪問索引:wide, 注:等,標(biāo)示一組指令,如:iload_代表iload_0,iload_1… - 運(yùn)算指令:
用于對(duì)兩個(gè)操作數(shù)棧上的值進(jìn)行特定運(yùn)算,并把結(jié)構(gòu)重寫存入操作數(shù)棧,指令如下:
加法:iadd,ladd,fadd,dadd
減法:isub,lsub,fsub,dsub
乘法:imul,lmul,fmul,dmul
除法:idiv,ldiv,fdiv,ddiv
求余:irem,lrem,frem,drem
取反:ineg,lneg,fneg,dneg
位移:ishl,ishr,iushr,lshl,lshr,lushr
按位或:ior,lor
按位與:iand,land
按位異或:ixor,lxor
局部變量:iinc
比較指令:dcmpg,dcmpl,fcmpg,fcmpl,lcmp
注:byte,short,char,boolean,沒有直接支持的算術(shù)指令,操作時(shí)使用int類型指令替換; 針對(duì)運(yùn)算數(shù)出現(xiàn)操作溢出,JVM并不會(huì)拋出異常,只會(huì)返回結(jié)果NaN(null的意思) - 類型轉(zhuǎn)換指令:
將兩種不同的數(shù)據(jù)類型進(jìn)行互相轉(zhuǎn)換,操作一般用于實(shí)現(xiàn)用戶代碼中的顯式轉(zhuǎn)換
直接支持寬化類型轉(zhuǎn)換,向上,小轉(zhuǎn)大
int->long,float,double等等
處理窄化類型轉(zhuǎn)換,必須顯式使用轉(zhuǎn)化指令
i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f - 對(duì)象創(chuàng)建于訪問指令:
創(chuàng)建對(duì)象:new
創(chuàng)建數(shù)組指令:newarray,anewarray,multianewarray
訪問類字段和實(shí)例字段指令:getfield,putfield,getstatic,putstatic
把一個(gè)數(shù)組元素加載到操作數(shù)棧:baload,caload,saload,iaload,laload,faload,daload,aaload
將一個(gè)操作數(shù)棧的值存儲(chǔ)到數(shù)組元素當(dāng)中:bastore,castore,sastore,iastore,fastore,dastore,aastore
取數(shù)組長(zhǎng)度:arraylength
檢查類實(shí)例類型:instanceof,checkcast
操作數(shù)棧管理指令:
將操作數(shù)棧的棧頂一個(gè)或兩個(gè)元素出棧:pop,pop2
復(fù)制棧頂一個(gè)或兩個(gè)數(shù)值并將復(fù)制值或者雙份的復(fù)制值重新壓入棧頂:dup,dup2,dup_x1,dup2_x1,dup_x2,dup_x2,dup2_x2
將棧最頂端的兩個(gè)數(shù)值交換:swap - 控制轉(zhuǎn)移指令:
條件分支:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonull,if_icmpeq,if_icmpne,if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne
復(fù)合條件分支:tableswitch,lookupswitch
無條件分支:goto,goto_w,jsr,jsr_w,ret - 方法調(diào)用和返回:
調(diào)用對(duì)象的實(shí)例方法:invokevirtual
調(diào)用接口方法:invokeinsterface
調(diào)用一些需要特殊處理的實(shí)例方法(實(shí)例初始化方法,私有方法,父類方法):invokespecial
調(diào)用靜態(tài)方法:invokestatic
在運(yùn)行時(shí)動(dòng)態(tài)解析出調(diào)用點(diǎn)限定符引用的方法,并執(zhí)行該方法:invokedynamic; 異常處理:athrow(throw) - 同步指令:
JVM支持方法及同步和方法內(nèi)部一段指令序列的同步,這兩種同步結(jié)構(gòu)都是使用Monitor來支持的
方法級(jí)的同步是隱式的,無需通過字節(jié)碼指令控制,它是現(xiàn)在方法調(diào)用和返回操作之中,JVM從方法常量池的方法表結(jié)構(gòu)中ACC_SYNCHRONIZED訪問標(biāo)志得知一個(gè)方法是否聲名為同步方法,當(dāng)方法調(diào)用,調(diào)用指令將會(huì)檢查方法的ACC_SYNCHRONIZED是否被設(shè)置,如果設(shè)置了,執(zhí)行線程就要求持有Monitor,然后才能執(zhí)行方法,當(dāng)方法完成時(shí)釋放Monitor
注:編譯器必須確保無論方法通過何種方式完成,方法中調(diào)用過得每條monitorenter指令都必須執(zhí)行對(duì)應(yīng)的monitorexit指令(無論方法正常/異常結(jié)束)
總結(jié)
以上是生活随笔為你收集整理的Java的Class类文件结构及基本字节码指令的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vim nerdtree 标签_7款vi
- 下一篇: 复习Java字节流_字符流使用及案例