1、Class類文件的結(jié)構(gòu)
class文件是一組以8位字節(jié)為基礎(chǔ)單位的二進(jìn)制流,各個(gè)數(shù)據(jù)項(xiàng)目嚴(yán)格按照順序緊湊的排列在class文件中,中間沒(méi)有添加任何分隔符。當(dāng)遇到需要占用8位字節(jié)以上空間的數(shù)據(jù)項(xiàng)時(shí),則按照高位在前的方式分割成若干個(gè)8位字節(jié)存儲(chǔ)。
class文件格式只有兩種數(shù)據(jù)類型:無(wú)符號(hào)數(shù)和表。
無(wú)符號(hào)數(shù)屬于基本數(shù)據(jù)類型,以u(píng)1、u2、u4、u8分別代表1個(gè)字節(jié)、2個(gè)字節(jié)、4個(gè)字節(jié)、8個(gè)字節(jié)的無(wú)符號(hào)數(shù),無(wú)符號(hào)數(shù)可以用來(lái)描述數(shù)字、索引引用、數(shù)量值或按照UTF-8編碼構(gòu)成的字符串值。
表是由多個(gè)無(wú)符號(hào)數(shù)或其他表作為數(shù)據(jù)項(xiàng)構(gòu)成的復(fù)合數(shù)據(jù)類型,以"_info"結(jié)尾,整個(gè)class文件就是一張表:

1.1 魔數(shù)與Class文件版本
(1) Class文件的魔數(shù)值是:0XCAFEBABE(音譯:咖啡寶貝)
(2) 緊接著魔數(shù)的4個(gè)字節(jié)是版本號(hào),第5和第6個(gè)字節(jié)是次版本號(hào)(Minor Version),第7和第8是主版本號(hào)(Major Version)
1.2 常量池
(1) 版本號(hào)之后是常量池入口,常量池是Class文件中的資源倉(cāng)庫(kù)
(2) 常量池入口放置的是constant_pool_count,計(jì)數(shù)從1開(kāi)始,第0項(xiàng)常量表示“不引用任何一個(gè)常量池項(xiàng)目”
(3) 常量池存放兩大類常量:字面量(Literal)和符號(hào)引用(Symbolic Reference),字面量包括文本字符串、聲明為final的常量值等,而符號(hào)引用包括3類:類和接口的全限定名、字段的名稱和描述符,方法的名稱和描述符,共有14種結(jié)構(gòu)不同的表數(shù)據(jù):

(4) 專門用于分析Class文件字節(jié)碼的工具:javap
1.3 訪問(wèn)標(biāo)志
在常量池之后,緊接著的兩個(gè)字節(jié)是訪問(wèn)標(biāo)志(access_flags),用于標(biāo)識(shí)類或接口的訪問(wèn)信息,含義見(jiàn)下表:

1.4 類索引、父類索引和接口索引
類索引(this_class)和父類索引(super_class)都是一個(gè)u2類型的數(shù)據(jù),而接口索引集合(interfaces)是一組u2類型數(shù)據(jù)的集合,Class文件中由這三項(xiàng)數(shù)據(jù)來(lái)確定這個(gè)類的繼承關(guān)系。查找過(guò)程:常量池索引值 -- CONSTANT_Class_info --?CONSTANT_Utf8_info :

1.5 字段表集合
字段表(field_info)用于描述接口或類中聲明的變量,包括類級(jí)變量(含static關(guān)鍵字)和實(shí)例級(jí)字段,不包括方法類的局部變量。字段表結(jié)構(gòu)如下:

access_flags如下圖:

name_index和descriptor_index都是對(duì)常量池的引用,分別代表字段的簡(jiǎn)單名稱和字段的描述符。描述符用來(lái)描述字段的數(shù)據(jù)類型,標(biāo)識(shí)字符含義如下:

對(duì)于數(shù)組類型,每一維度使用前置"["表示,如String[][]將被記錄為[[Ljava/lang/String,int[]表示[I
1.6 方法表集合

其中name_index(簡(jiǎn)單名稱)代表沒(méi)有類型和參數(shù)修飾的方法,descriptor_index(描述符)用來(lái)描述方法的參數(shù)列表(包括數(shù)量、類型和順序)和返回值

方法中的代碼存放在"Code"屬性表里,編譯器自動(dòng)添加的方法:類構(gòu)造器<clinit>方法和實(shí)例構(gòu)造器<init>方法
1.7 屬性表集合
詳見(jiàn):https://www.cnblogs.com/flyingcr/p/10428285.html
2、字節(jié)碼指令簡(jiǎn)介
字節(jié)碼指令由一個(gè)字節(jié)長(zhǎng)度,代表著某種特定操作含義的數(shù)字(操作碼,Opcode),以及其后0至多個(gè)此操作所需參數(shù)(操作數(shù),Operands)構(gòu)成
2.1 字節(jié)碼與數(shù)據(jù)類型
大多數(shù)的指令包含了其操作所對(duì)應(yīng)的數(shù)據(jù)類型信息,如iload指令用于從局部變量表加載int型數(shù)據(jù)到操作數(shù)棧,而fload用于加載float型數(shù)據(jù)。助記符與數(shù)據(jù)類型對(duì)代應(yīng)關(guān)系:i代表int,l代表long,s代表short,b代表byte,c代表char,f代表float,d代表double,a代表reference。
2.2 加載和存儲(chǔ)指令
加載和存儲(chǔ)指令用于將數(shù)據(jù)在棧幀中的局部變量表和操作數(shù)棧之間來(lái)回傳輸。load代表將局部變量加載到操作數(shù)棧,store將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量表,push和const代表將常量加載到操作數(shù)棧
2.3 運(yùn)算指令
加法指令: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 iushrl shl lshr lushr
按位或指令:ior lor
按位與指令:iand land
按位異或指令:ixor lxor
局部變量自增指令:iinc
比較指令:dcmpg dcmpl fcmpg fcmpl lcmp
2.4 類型轉(zhuǎn)化指令
窄化類型轉(zhuǎn)化時(shí),需顯示的使用轉(zhuǎn)化指令:i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l和d2f
2.5 對(duì)象創(chuàng)建和訪問(wèn)指令
創(chuàng)建類實(shí)例的指令:new
創(chuàng)建數(shù)組的指令:newarray(基本類型數(shù)組)、anewarray(引用類型數(shù)組)、multianewarray(多維度引用類型數(shù)組)
訪問(wèn)類字段的指令:getstatic、putstatic
訪問(wèn)實(shí)例字段的指令:getfield、putfield
將數(shù)組加載到操作數(shù)棧的指令:aload
將操作數(shù)棧的值存儲(chǔ)到數(shù)組元素的指令:astore
取數(shù)組元素長(zhǎng)度的指令:arraylength
檢查實(shí)例類型的指令:instanceof、checkcast
2.6 操作數(shù)棧管理指令
pop:棧頂一個(gè)元素出棧,pop2:棧頂兩個(gè)元素出棧
dup:復(fù)制棧頂一個(gè)數(shù)值并將復(fù)制值重新壓入棧頂,dup2:復(fù)制棧頂兩個(gè)數(shù)值并將雙份復(fù)制值重新壓入棧頂
swap:將棧頂?shù)膬蓚€(gè)數(shù)值互換
2.7 控制轉(zhuǎn)移指令



2.8 方法調(diào)用和返回指令
invokevirtual:用于調(diào)用對(duì)象的實(shí)例方法,根據(jù)對(duì)象的實(shí)際類型進(jìn)行分派
invokeinterface:用于調(diào)用接口方法,找到實(shí)現(xiàn)該接口合適的類的方法
invokespecial:用于調(diào)用實(shí)例初始化方法、私有方法和父類方法
invokestatic:用于調(diào)用static方法
invokedynamic:用于在運(yùn)行時(shí)動(dòng)態(tài)解析出調(diào)用點(diǎn)限定符所引用的方法,并執(zhí)行該方法
2.9 異常處理指令
顯示拋出異常的操作(throw語(yǔ)句)都由athrow指令實(shí)現(xiàn)
2.10 同步指令
Java虛擬機(jī)支持方法級(jí)和方法內(nèi)部一段指令序列的同步,這兩種同步結(jié)構(gòu)都是使用管程(Monitor)來(lái)支持的。
方法級(jí)的同步是隱式,則無(wú)需通過(guò)字節(jié)碼指令來(lái)控制,它實(shí)現(xiàn)在方法調(diào)用和返回操作之中。虛擬機(jī)可以從方法常量池的方法表結(jié)構(gòu)中的ACC_SYNCHRONIZED訪問(wèn)標(biāo)志區(qū)分一個(gè)方法是否是同步方法。當(dāng)方法調(diào)用時(shí),調(diào)用指令將會(huì)檢查方法的ACC_SYNCHRONIZED訪問(wèn)標(biāo)志是否被設(shè)置,如果設(shè)置了,執(zhí)行線程先持有管程,然后再執(zhí)行方法,最后在方法完成(無(wú)論是正常完成還是非正常完成)時(shí)釋放管程。在方法執(zhí)行期間,執(zhí)行線程持有了管程,其他線程都無(wú)法再獲得同一個(gè)管程。如果一個(gè)同步方法執(zhí)行期間拋出了異常,并且在方法內(nèi)部無(wú)法處理異常,那這個(gè)同步方法所持有的管程將在異常拋到同步方法之外時(shí)自動(dòng)釋放。
同步指令由synchronized塊來(lái)表示的,Java虛擬機(jī)的指令集中有monitorenter和monitorexit兩條指令來(lái)支持synchronized關(guān)鍵字的語(yǔ)義,正確實(shí)現(xiàn)synchronized關(guān)鍵字需要編譯器與Java虛擬機(jī)兩者協(xié)作支持。