一年又一年会通,字節(jié)跳動(dòng) Lark(飛書) 研發(fā)團(tuán)隊(duì)又雙叒叕開始招新生啦杜耙!
【內(nèi)推碼】:GTPUVBA
【內(nèi)推鏈接】:https://job.toutiao.com/s/JRupWVj
【招生對(duì)象】:20年9月后~21年8月前 畢業(yè)的同學(xué)
【報(bào)名時(shí)間】:6.16-7.16(提前批簡(jiǎn)歷投遞只有一個(gè)月抓住機(jī)會(huì)哦3砂瘛)
【畫重點(diǎn)】:提前批和正式秋招不矛盾桐智!面試成功凌外,提前鎖定Offer辩尊;若有失利,額外獲得一次面試機(jī)會(huì)康辑,正式秋招開啟后還可再次投遞摄欲。
點(diǎn)擊進(jìn)入我的博客
4.1 字節(jié)碼
平臺(tái)無(wú)關(guān):Sun公司以及其他的虛擬機(jī)提供商發(fā)布了許多可以運(yùn)行在各種不同平臺(tái)上的虛擬機(jī),這些虛擬機(jī)都可以載入和執(zhí)行同一種平臺(tái)無(wú)關(guān)的字節(jié)碼疮薇,從而實(shí)現(xiàn)了程序的“一次編寫,到處運(yùn)行”胸墙。
語(yǔ)言無(wú)關(guān):語(yǔ)言無(wú)關(guān)的基礎(chǔ)是虛擬機(jī)和字節(jié)碼存儲(chǔ)格式,Java虛擬機(jī)不和任何語(yǔ)言(包括Java)綁定惦辛,它只與Class文件這種特定的二進(jìn)制文件格式所關(guān)聯(lián)劳秋,Class文件中包含了Java虛擬機(jī)指令集和符號(hào)表以及若干其他輔助信息。
4.2 Class類文件的結(jié)構(gòu)
Class文件是一組以8位字節(jié)為基礎(chǔ)單位的二進(jìn)制流,各個(gè)數(shù)據(jù)項(xiàng)目嚴(yán)格按照順序緊湊地排列在Class文件之中玻淑,中間沒有添加任何分隔符嗽冒,這使得整個(gè)Class文件中存儲(chǔ)的內(nèi)容幾乎全部是程序運(yùn)行的必要數(shù)據(jù),沒有空隙存在补履。當(dāng)遇到需要占用8位字節(jié)以上空間的數(shù)據(jù)項(xiàng)時(shí)添坊,則會(huì)按照高位在前的方式分割成若干個(gè)8位字節(jié)進(jìn)行存儲(chǔ)。
- Class文件只有兩種數(shù)據(jù)類型:無(wú)符號(hào)數(shù)箫锤、表贬蛙。
- 無(wú)符號(hào)數(shù):無(wú)符號(hào)數(shù)屬于基本的數(shù)據(jù)類型,以u(píng)1谚攒、u2阳准、u4、u8來(lái)分別代表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ù)類型帮寻,表習(xí)慣性以_info結(jié)尾乍狐。表用于描述有層次的復(fù)合結(jié)構(gòu)的數(shù)據(jù),整個(gè)Class文件本質(zhì)上就是一張表固逗,由以下的數(shù)據(jù)項(xiàng)構(gòu)成浅蚪。
- 容量計(jì)數(shù)器:無(wú)論是無(wú)符號(hào)數(shù)還是表,當(dāng)需要描述同一類型但數(shù)量不定的多個(gè)數(shù)據(jù)時(shí)抒蚜,經(jīng)常會(huì)使用一個(gè)前置的容量計(jì)數(shù)器加若干連續(xù)的數(shù)據(jù)項(xiàng)的形式掘鄙。
4.2.1 魔數(shù)與Class文件的版本
魔數(shù):每個(gè)Class文件的頭4個(gè)字節(jié)稱為魔數(shù)(Magic Number),其唯一作用是確定這個(gè)文件是否為一個(gè)能被虛擬機(jī)接受的Class文件嗡髓。值為0xCAFEBABE操漠。
Class的版本號(hào):緊接著魔數(shù)的4個(gè)字節(jié)存儲(chǔ)的是Class的版本號(hào)——第5個(gè)和第6個(gè)字節(jié)是次版本號(hào)(Minor Version),第7個(gè)和第8個(gè)字節(jié)是主版本號(hào)(Major Version)饿这。
版本號(hào)兼容:高版本的JDK只能向下兼容以前版本的Class文件浊伙,不能運(yùn)行以后版本的Class文件。
4.2.2 常量池
常量池:緊接著主次版本號(hào)后的是常量池长捧,也可以理解為Class文件的資源倉(cāng)庫(kù)嚣鄙,它是與其他項(xiàng)目關(guān)聯(lián)最多的數(shù)據(jù)類型,也是占用Class文件空間最大的數(shù)據(jù)項(xiàng)目之一串结,同時(shí)還算第一個(gè)出現(xiàn)的表類型數(shù)據(jù)項(xiàng)目哑子。
常量池計(jì)數(shù)值:由于常量池中常量數(shù)量不固定舅列,因此在入口處要放置一項(xiàng)u2類型的數(shù)據(jù),代表常量池計(jì)數(shù)值(從1開始卧蜓,因?yàn)橛?jì)數(shù)的0代表“不引用任何一個(gè)常量池項(xiàng)目”的含義)帐要。
常量池存放數(shù)據(jù):常量池中主要存放兩大類常量——字面量(Literal)和符號(hào)引用(Symbolic References)。字面量比較接近于Java語(yǔ)言層面的常量概念——如文本字符串弥奸、聲明為final的常量值等榨惠。符號(hào)引用則屬于編譯原理方面的概念,包括下面三類常量:類和接口的全限定名(Fully Qualified Name)盛霎、字段的名稱和描述符(Descriptor)赠橙、方法的名稱和描述符。
動(dòng)態(tài)連接:Java代碼在javac編譯的時(shí)候愤炸,并沒有連接這一步驟期揪,而是在虛擬機(jī)加載Class文件的時(shí)候動(dòng)態(tài)連接。
常量池中的項(xiàng):常量池中每一項(xiàng)都是一個(gè)表摇幻,截止到JDK 7中更用14種各不相同的表結(jié)構(gòu)數(shù)據(jù)横侦,其共同特點(diǎn)就是表開始的第一位是一個(gè)u1類型的標(biāo)識(shí)位。
4.2.3 訪問標(biāo)志
在常量池結(jié)束之后绰姻,緊接著的兩個(gè)字節(jié)代表訪問標(biāo)志(access_flags),這個(gè)標(biāo)志用于識(shí)別一些類或者接口層次的訪問信息引瀑,包括:這個(gè)Class是類還是接口狂芋;是否定義為public類型;是否定義為abstract類型憨栽;如果是類的話帜矾,是否被聲明為final等。
4.2.4 類索引屑柔、父類索引屡萤、接口索引
類索引和父類索引:是一個(gè)u2類型的數(shù)據(jù),用于確定這個(gè)類的全限定類名和父類的全限定類名掸宛,指向一個(gè)類型為CONSTANT_Class_info的類描述符常量死陆,通過CONSTANT_Class_info類型的常量中的索引類型可以找到定義在CONSTANT_Utf8_info類型的常量中的全限定名字符串。
接口索引集合:是一組u2類型的數(shù)據(jù)集合唧瘾,用于描述這個(gè)類實(shí)現(xiàn)了哪些接口措译,這些被實(shí)現(xiàn)的接口按照從左到右排列在接口索引集合中。入口的第一項(xiàng)——u2類型的數(shù)據(jù)為接口計(jì)數(shù)器饰序,表示索引表的容量领虹;如果沒有實(shí)現(xiàn)任何接口,則該計(jì)數(shù)器為0求豫。
4.2.5 字段表集合
字段表:字段表(field_info)用于描述接口或者類中聲明的變量塌衰。字段(field)包括類級(jí)變量以及實(shí)例級(jí)變量诉稍,但不包括在方法內(nèi)部聲明的局部變量。
一個(gè)字段包括的信息有:字段的作用域(public最疆、private均唉、protected修飾符)、是實(shí)例變量還是類變量(static修飾符)肚菠、可變性(final)舔箭、并發(fā)可見性(volatile修飾符,是否強(qiáng)制從主內(nèi)存讀寫)蚊逢、可否被被序列化(transient修飾符)层扶、字段數(shù)據(jù)類型(基本類型、對(duì)象烙荷、數(shù)組)镜会、字段名稱。
修飾符布爾值:上述這些信息中终抽,各個(gè)修飾符都是布爾值戳表,要么有某個(gè)修飾符,要么沒有昼伴,很適合使用標(biāo)志位來(lái)表示匾旭。而字段叫什么名字、字段被定義為什么數(shù)據(jù)類型圃郊,這些都是無(wú)法固定的价涝,只能引用常量池中的常量來(lái)描述。
字段表結(jié)構(gòu)
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
字段訪問標(biāo)志
標(biāo)志名稱 | 標(biāo)志值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 字段是否public |
ACC_PRIVATE | 0x0002 | 字段是否private |
ACC_PROTECTED | 0x0004 | 字段是否protected |
ACC_STATIC | 0x0008 | 字段是否static |
ACC_FINAL | 0x0010 | 字段是否final |
ACC_VOLATILE | 0x0040 | 字段是否volatile |
ACC_TRANSIENT | 0x0080 | 字段是否transient |
ACC_SYNTHETIC | 0x1000 | 字段是否由編譯器自動(dòng)產(chǎn)生的 |
ACC_ENUM | 0x4000 | 字段是否enum |
name_index
name_index是對(duì)常量池的引用持舆,代表著字段的簡(jiǎn)單名稱色瘩。簡(jiǎn)單名稱是指沒有類型和參數(shù)修飾的方法或者字段名稱,這個(gè)類中的inc()方法和m字段的簡(jiǎn)單名稱分別是“inc”和“m”逸寓。
全限定名:以下面代碼為例居兆,“org/xxx/clazz/TestClass”是這個(gè)類的全限定名,僅僅是把類全名中的“.”替換成了“/”而已竹伸,為了使連續(xù)的多個(gè)全限定名之間不產(chǎn)生混淆泥栖,在使用時(shí)最后一般會(huì)加入一個(gè)“;”表示全限定名結(jié)束。
public class TestClass {
private int m;
public int inc() {
return m + 1;
}
}
descriptor_index
descriptor_index也是對(duì)常量池的引用佩伤,代表著字段和方法的描述符聊倔。描述符的作用是用來(lái)描述字段的數(shù)據(jù)類型、方法的參數(shù)列表(包括數(shù)量生巡、類型以及順序)和返回值耙蔑。根據(jù)描述符規(guī)則,基本數(shù)據(jù)類型(byte孤荣、char甸陌、double须揣、float、int钱豁、long耻卡、short、boolean)以及代表無(wú)返回值的void類型都用一個(gè)大寫字符來(lái)表示牲尺,而對(duì)象類型則用字符L加對(duì)象的全限定名來(lái)表示卵酪。
標(biāo)識(shí)字符 | 含義 | 標(biāo)識(shí)字符 | 含義 |
---|---|---|---|
B | 基本類型byte | J | 基本類型long |
C | 基本類型char | S | 基本類型short |
D | 基本類型double | Z | 基本類型boolean |
F | 基本類型float | V | 特殊類型void |
I | 基本類型int | L | 對(duì)象類型,如Ljava/lang/Object |
數(shù)組類型:每一維度將使用一個(gè)前置的“[”字符來(lái)描述谤碳,如一個(gè)定義為“java.lang.String[][]”類型的二維數(shù)組溃卡,將被記錄為:“[[Ljava/lang/String;”,蜒简,一個(gè)整型數(shù)組“int[]”被記錄為“[I”瘸羡。
描述方法:按照先參數(shù)列表,后返回值的順序描述搓茬,參數(shù)列表按照參數(shù)的嚴(yán)格順序放在一組小括號(hào)“( )”之內(nèi)犹赖。如方法void inc()的描述符為“( ) V”,方法java.lang.String toString()的描述符為“( ) LJava/lang/String;”卷仑,方法int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)的描述符為“([CII[CIII) I”峻村。
attributes_count與attribute_info
- 字段表都包含的固定數(shù)據(jù)項(xiàng)目到descriptor_index為止就結(jié)束了,不過在descriptor_index之后跟隨著一個(gè)屬性表集合用于存儲(chǔ)一些額外的信息系枪,字段都可以在屬性表中描述零至多項(xiàng)的額外信息雀哨。對(duì)于本例中的字段m,他的屬性表計(jì)數(shù)器為0私爷,也就是說沒有需要額外描述的信息,但是膊夹,如果將字段m的聲明改為“final static int m=123”衬浑,那就可能會(huì)存在一項(xiàng)名稱為ConstantValue的屬性洞翩,其值指向常量123役纹。
- 字段表集合中不會(huì)列出從超類或者父接口中繼承而來(lái)的字段,但有可能列出原本Java代碼之中不存在的字段隧饼,譬如在內(nèi)部類中為了保持對(duì)外部類的訪問性进统,會(huì)自動(dòng)添加指向外部類實(shí)例的字段助币。
- 在Java語(yǔ)言中字段是無(wú)法重載的,兩個(gè)字段的數(shù)據(jù)類型螟碎、修飾符不管是否相同眉菱,都必須使用不一樣的名稱,但是對(duì)于字節(jié)碼來(lái)講掉分,如果兩個(gè)字段的描述符不一致俭缓,那字段重名就是合法的克伊。
4.2.6 方法表集合
方法表的結(jié)構(gòu)如同字段表一樣,依次包括了訪問標(biāo)志(access_flags)华坦、名稱索引(name_index)愿吹、描述符索引(descriptor_index)、屬性表結(jié)合(attributes)幾項(xiàng)惜姐,如字段表所示犁跪。
方法訪問標(biāo)志
標(biāo)志名稱 | 標(biāo)志值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 方法是否為public |
ACC_PRIVATE | 0x0002 | 方法是否為private |
ACC_PROTECTED | 0x0004 | 方法是否為protected |
ACC_STATIC | 0x0008 | 方法是否為static |
ACC_FINAL | 0x0010 | 方法是否為final |
ACC_SYNCHRONIZED | 0x0020 | 方法是否為synchronized |
ACC_BRIDGE | 0x0040 | 方法是否由編譯器產(chǎn)生的橋接方法 |
ACC_VARARGS | 0x0080 | 方法是否接受不定參數(shù) |
ACC_NATIVE | 0x0100 | 方法是否為native |
ACC_ABSTRACT | 0x0400 | 方法是否為abstract |
ACC_STRICTFP | 0x0800 | 方法是否為strictfp |
ACC_SYNTHETIC | 0x1000 | 方法是否由編譯器自動(dòng)產(chǎn)生的 |
方法里的代碼
方法里的Java代碼,經(jīng)過編譯器編譯成字節(jié)碼指令后歹袁,存放在方法屬性集合中一個(gè)名為“Code”的屬性里面坷衍,屬性表作為Class文件格式中最具擴(kuò)展性的一種數(shù)據(jù)項(xiàng)目。
重寫
與字段表集合相對(duì)應(yīng)的宇攻,如果父類方法在子類匯總沒有被重寫(Override)惫叛,方法表集合中就不會(huì)出現(xiàn)來(lái)自父類的方法信息。
自動(dòng)添加方法
有可能會(huì)出現(xiàn)由編譯器自動(dòng)添加的方法逞刷,最典型的便是類構(gòu)造器“<clinit>”方法和實(shí)例構(gòu)造器“<init>”方法嘉涌。
重載
在Java語(yǔ)言中,要重載(Overload)一個(gè)方法夸浅,除了要與原方法具有相同的簡(jiǎn)單名稱之外仑最,還要求必須擁有一個(gè)與原方法不同的特征簽名,特征簽名就是一個(gè)方法中各個(gè)參數(shù)在常量池中的字段符號(hào)引用的集合帆喇,也就是因?yàn)榉祷刂挡粫?huì)包含在特征簽名中警医,因此Java語(yǔ)言里面是無(wú)法僅僅依靠返回值的不同來(lái)對(duì)一個(gè)已有方法進(jìn)行重載的。但是在Class文件格式匯總坯钦,特征簽名的范圍更大一些预皇,只要描述符不是完全一致的兩個(gè)方法也可以共存。也就是說婉刀,如果兩個(gè)方法有相同的名稱和特征簽名吟温,但返回值不同,那么也是可以合法共存于同一個(gè)Class文件中的突颊。
4.2.7 屬性表集合
在Class文件鲁豪、字段表、方法表中都可以攜帶自己的屬性表集合律秃,以用于描述某些場(chǎng)景專有的信息爬橡。與Class文件中其他的數(shù)據(jù)項(xiàng)目要求嚴(yán)格的順序、長(zhǎng)度和內(nèi)容不同棒动,屬性表集合的限制稍微寬松了一些糙申,不再要求各個(gè)屬性表具有嚴(yán)格順序,并且只要不與已有屬性名重復(fù)迁客,任何人實(shí)現(xiàn)的編譯器都可以向?qū)傩员韺懭胱约憾x的屬性信息郭宝,Java虛擬機(jī)運(yùn)行時(shí)會(huì)忽略掉他不認(rèn)識(shí)的屬性辞槐。
屬性表的結(jié)構(gòu)
屬性名稱需要從常量池中引用一個(gè)CONSTANT_Utf8_info類型的常量來(lái)表示,而屬性的結(jié)構(gòu)則是完全自定義的粘室,只需要通過一個(gè)u4的長(zhǎng)度屬性去說明屬性值所占用的位數(shù)即可榄檬。一個(gè)符合規(guī)則的屬性表應(yīng)該滿足下表所定義的結(jié)構(gòu):
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u1 | info | attribute_length |
虛擬機(jī)規(guī)范預(yù)定義的屬性
屬性名稱 | 使用位置 | 含義 |
---|---|---|
Code | 方法表 | Java代碼編譯成的字節(jié)碼指令 |
ConstantValue | 字段表 | final關(guān)鍵字定義的常量值 |
Deprecated | 類、方法表衔统、字段表 | 被聲明為deprecated的方法和字段 |
Exceptions | 方法表 | 方法拋出的異常 |
EnclosingMethod | 類文件 | 僅當(dāng)一個(gè)類為局部類或者匿名類時(shí)才能擁有這個(gè)屬性鹿榜,這個(gè)屬性用于標(biāo)識(shí)這個(gè)類所在的外圍方法 |
InnerClasses | 類文件 | 內(nèi)部類列表 |
LineNumberTable | Code屬性 | Java源碼的行號(hào)與字節(jié)碼指令的對(duì)用關(guān)系 |
LocalVariableTable | Code屬性 | 方法的局部變量描述 |
StackMapTable | Code屬性 | JDK1.6中新增的屬性,供新的類型檢查驗(yàn)證器(Type Checker)檢查和處理目標(biāo)方法的局部變量和操作數(shù)棧所需要的類型是否匹配 |
Signature | 類锦爵、方法表舱殿、字段表 | JDK1.5中新增的屬性,這個(gè)屬性用于支持泛型情況下的方法簽名险掀,在Java語(yǔ)言中沪袭,任何類、接口樟氢、初始化方法或成員的泛型簽名如果包含了類型變量(Type Variables)或參數(shù)化類型(Parameterized Types)冈绊,則Signature屬性會(huì)為他記錄泛型簽名信息。由于Java的泛型采用擦除法實(shí)現(xiàn)埠啃,在為了避免類型信息被擦出后導(dǎo)致簽名混亂死宣,需要這個(gè)屬性記錄泛型中的相關(guān)信息 |
SourceFile | 類文件 | 記錄源文件名稱 |
SourceDebugExtension | 類文件 | JDK 1.6中新增的屬性,SourceDebugExtension屬性用于存儲(chǔ)額外的調(diào)試信息碴开,譬如在進(jìn)行JSP文件調(diào)試時(shí)毅该,無(wú)法同構(gòu)Java堆棧來(lái)定位到JSP文件的行號(hào),JSR-45規(guī)范為這些非Java語(yǔ)言編寫潦牛,卻需要編譯成字節(jié)碼并運(yùn)行在Java虛擬機(jī)中的程序提供了一個(gè)進(jìn)行調(diào)試的標(biāo)準(zhǔn)機(jī)制眶掌,使用SourceDebugExtension屬性就可以用于存儲(chǔ)這個(gè)標(biāo)準(zhǔn)所新加入的調(diào)試信息 |
Synthetic | 類、方法表巴碗、字段表 | 標(biāo)識(shí)方法或字段為編譯器自動(dòng)生成的 |
LocalVariableTypeTable | 類 | JDK 1.5中新增的屬性畏线,他使用特征簽名代替描述符,是為了引入泛型語(yǔ)法之后能描述泛型參數(shù)化類型而添加 |
RuntimeVisibleAnnotations | 類良价、方法表、字段表 | JDK 1.5中新增的屬性蒿叠,為動(dòng)態(tài)注解提供支持明垢。RuntimeVisibleAnnotations屬性用于指明哪些注解是運(yùn)行時(shí)(實(shí)際上運(yùn)行時(shí)就是進(jìn)行反射調(diào)用)可見的 |
RuntimeInVisibleAnnotations | 類、方法表市咽、字段表 | JDK 1.5新增的屬性痊银,與RuntimeVisibleAnnotations屬性作用剛好相反,用于指明哪些注解是運(yùn)行時(shí)不可見的 |
RuntimeVisibleParameter Annotations | 方法表 | JDK 1.5新增的屬性施绎,作用與RuntimeVisibleAnnotations屬性類似溯革,只不過作用對(duì)象為方法參數(shù) |
RuntimeInVisibleAnnotations Annotations | 方法表 | JDK 1.5中新增的屬性贞绳,作用與RuntimeInVisibleAnnotations屬性類似,只不過作用對(duì)象為方法參數(shù) |
AnnotationDefault | 方法表 | JDK 1.5中新增的屬性致稀,用于記錄注解類元素的默認(rèn)值 |
BootstrapMethods | 類文件 | JDK 1.7中新增的屬性冈闭,用于保存invokedynamic指令引用的引導(dǎo)方法限定符 |
Code屬性
Code屬性是Class文件中最重要的一個(gè)屬性,如果把一個(gè)Java程序中的信息分為代碼(Code抖单,方法體里面的Java代碼)和元數(shù)據(jù)(Metadata萎攒,包括類、字段矛绘、方法定義及其他信息)兩部分耍休,那么在整個(gè)Class文件中,Code屬性用于描述代碼货矮,所有的其他數(shù)據(jù)項(xiàng)目都用于描述元數(shù)據(jù)羊精。
Java程序方法體中的代碼經(jīng)過Javac編譯器處理后,最終變?yōu)樽止?jié)碼指令存儲(chǔ)在Code屬性內(nèi)囚玫。Code屬性出現(xiàn)在方法表的屬性集合之中喧锦,但并非所有的方法表都必須存在這個(gè)屬性,譬如接口或者抽象類中的方法就不存在Code屬性劫灶。如果方法表有Code屬性存在裸违,那么他的結(jié)構(gòu)將如下表所示。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | max_stack | 1 |
u2 | max_locals | 1 |
u4 | code_length | 1 |
u1 | code | code_length |
u2 | exception_table_length | 1 |
exception_info | exception_table | exception_table_length |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
- attribute_name_index:是一項(xiàng)指向CONSTANT_Utf8_info型常量的索引本昏,常量值固定為“Code”供汛,他代表了該屬性的屬性名稱。
- attribute_length:指示了屬性值的長(zhǎng)度涌穆,由于屬性名稱索引與屬性長(zhǎng)度一共為6個(gè)字節(jié)怔昨,所以屬性值的長(zhǎng)度固定為整個(gè)屬性表長(zhǎng)度減少6個(gè)字節(jié)。
- max_stack:代表了操作數(shù)棧(Operand Stacks)深度的最大值宿稀。在方法執(zhí)行的任意時(shí)刻趁舀,操作數(shù)棧都不會(huì)超過這個(gè)深度。虛擬機(jī)運(yùn)行的時(shí)候需要根據(jù)這個(gè)值分配棧幀(Stack Frame)中的操作幀深度祝沸。
- max_locals:代表了局部變量表所需的存儲(chǔ)空間矮烹。在這里,max_locals的單位是Slot罩锐,Slot是虛擬機(jī)為局部變量分配內(nèi)存所使用的最小單位奉狈。對(duì)于byte、char涩惑、float仁期、int、short、boolean和returnAddress等長(zhǎng)度不超過32位的數(shù)據(jù)類型跛蛋,每個(gè)局部變量占用1個(gè)Slot熬的,而double和long這兩種64位的數(shù)據(jù)類型則需要兩個(gè)Slot來(lái)存放。方法參數(shù)(包括實(shí)例方法中的隱藏參數(shù)“this”)赊级、顯式異常處理器的參數(shù)(Exception Handler Parameter押框,就是try-catch語(yǔ)句中catch塊所定義的異常)、方法體中定義的局部變量都需要使用局部變量表來(lái)存放此衅。另外强戴,并不是在方法中用到了多少個(gè)局部變量,就把這些局部變量所占Slot之和作為max_locals的值挡鞍,原因是局部變量表中的Slot可以重用骑歹,當(dāng)代碼執(zhí)行超出一個(gè)局部變量的作用域時(shí),這個(gè)局部變量所占的Slot可以被其他局部變量所使用墨微,Javac編譯器會(huì)根據(jù)變量的作用域來(lái)分配Slot給各個(gè)變量使用道媚,然后計(jì)算出max_locals的大小。
- code_length和code:用來(lái)存儲(chǔ)java源程序編譯后生成的字節(jié)碼指令翘县。code_length代表字節(jié)碼長(zhǎng)度最域,code是用于存儲(chǔ)字節(jié)碼指令的一系列字節(jié)流。既然叫字節(jié)碼指令锈麸,那么每個(gè)指令就是一個(gè)u1類型的單字節(jié)镀脂,當(dāng)虛擬機(jī)讀取到code中的一個(gè)字節(jié)碼時(shí),就可以對(duì)應(yīng)找出這個(gè)字節(jié)碼代表的是什么指令忘伞,并且可以知道這條指令后面是否需要跟隨參數(shù)薄翅,以及參數(shù)應(yīng)當(dāng)如何理解。我們知道一個(gè)u1數(shù)據(jù)類型的取值范圍為0x000xFF氓奈,對(duì)應(yīng)十進(jìn)制的0255翘魄,也就是一共可以表達(dá)256條指令,目前舀奶,Java虛擬機(jī)規(guī)范已經(jīng)定義了其中約200條編碼值對(duì)應(yīng)的指令含義暑竟。
- 關(guān)于code_length:有一件值得注意的事情,雖然他是一個(gè)u4類型的長(zhǎng)度值育勺,理論上最大值可以達(dá)到2的32次方減1但荤,但是虛擬機(jī)規(guī)范中明確限制了一個(gè)方法不允許超過65535條字節(jié)碼指令,即他實(shí)際只使用了u2的長(zhǎng)度涧至,如果超過這個(gè)限制纱兑,Javac編譯器也會(huì)拒絕編譯。一般來(lái)講化借,編寫Java代碼時(shí)只要不是刻意去編寫一個(gè)超長(zhǎng)的方法來(lái)為難編譯器,是不太可能超過這個(gè)最大值的限制捡多。但是蓖康,某些特殊情況铐炫,例如在編譯一個(gè)很復(fù)雜的JSP文件時(shí),某些JSP編譯器會(huì)把JSP內(nèi)容和頁(yè)面輸出的信息歸并于一個(gè)方法之中蒜焊,就可能因?yàn)榉椒ㄉ勺止?jié)碼超長(zhǎng)的原因而導(dǎo)致編譯失敗倒信。
Exceptions屬性
這里的Exceptions屬性是在方法表與Code屬性平級(jí)的一項(xiàng)屬性。Exceptions屬性的作用是列舉出方法中可能拋出的受查異常(Checked Exceptions)泳梆,也就是說方法描述時(shí)在throws關(guān)鍵字啊后面列舉的異常鳖悠。他的結(jié)構(gòu)見下表。
類型 | 名稱 | 數(shù)量 | 類型 | 名稱 | 數(shù)量 |
---|---|---|---|---|---|
u2 | attribute_name_index | 1 | u2 | number_of_exceptions | 1 |
u4 | attribute_length | 1 | u2 | exception_index_table | number_of_exceptions |
- number_of_exceptions:項(xiàng)表示方法可能拋出number_of_exceptions種受查異常
- exception_index_table:每一種受查異常使用一個(gè)exception_index_table項(xiàng)表示优妙,exception_index_table是一個(gè)指向常量池中CONSTANT_Class_info型常量的索引乘综,代表了該受查異常的類型。
LineNumberTable屬性
LineNumberTable屬性用于描述Java源碼行號(hào)與字節(jié)碼行號(hào)(字節(jié)碼的偏移量)之間的對(duì)應(yīng)關(guān)系套硼。他并不是運(yùn)行時(shí)必須的屬性卡辰,但默認(rèn)生成到Class文件之中,可以在Javac中分別使用-g : none或-g : lines選項(xiàng)來(lái)取消或要求生成這項(xiàng)信息邪意。如果選擇不生成LineNumberTable屬性九妈,對(duì)程序運(yùn)行產(chǎn)生的最主要的影響就是當(dāng)拋出異常時(shí),堆棧中將不會(huì)顯示出錯(cuò)的行號(hào)雾鬼,并且在調(diào)試程序的時(shí)候萌朱,也無(wú)法按照源碼行來(lái)設(shè)置斷點(diǎn)。LineNumberTable屬性的結(jié)構(gòu)見下表策菜。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | line_number_table_length | 1 |
line_number_info | line_number_table | line_number_table_length |
- line_number_table:是一個(gè)數(shù)量為line_number_table_length晶疼、類型為line_number_info的集合
- line_number_info表:包括了start_pc和line_number兩個(gè)u2類型的數(shù)據(jù)項(xiàng),前者是字節(jié)碼行號(hào)做入,后者是Java源碼行號(hào)冒晰。
LocalVariableTable屬性
LocalVariableTable屬性用于描述棧幀中局部變量表中的變量與Java源碼中定義的變量之間的關(guān)系,她也不是運(yùn)行時(shí)必須的屬性竟块,但默認(rèn)會(huì)生成到Class文件之中壶运,可以在Javac中分別使用-g : none或-g :vars選項(xiàng)來(lái)取消或要求生成這項(xiàng)信息。如果沒有生成這項(xiàng)屬性浪秘,最大的影響就是當(dāng)前其他人引用這個(gè)方法時(shí)蒋情,所有的參數(shù)名稱都將會(huì)丟失,IDE將會(huì)使用諸如arg0耸携、arg1之類的占位符代替原有的參數(shù)名棵癣,這對(duì)程序運(yùn)行沒有影響,但是會(huì)對(duì)代碼編寫帶來(lái)較大不便夺衍,而且在調(diào)試期間無(wú)法根據(jù)參數(shù)名稱從上下文中獲得參數(shù)值狈谊。LocalVariableTable屬性的結(jié)構(gòu)見下表。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | local_variable_table_length | 1 |
local_variable_info | local_variable_table | local_variable_table_length |
u2 | start_pc | 1 |
u2 | length | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | index | 1 |
- start_pc和length:屬性分別代表了這個(gè)局部變量的生命周期開始地字節(jié)碼偏移量及其作用范圍覆蓋的長(zhǎng)度,兩者結(jié)合起來(lái)就是這個(gè)局部變量在字節(jié)碼之中的作用域范圍河劝。
- name_index和descriptor_index:都是指向常量池中CONSTANT_Utf8_info型常量的索引壁榕,分別代表了局部變量的名稱以及這個(gè)局部變量的描述符。
- index:是這個(gè)局部變量在棧幀局部變量表中Slot的位置赎瞎。當(dāng)這個(gè)變量數(shù)據(jù)類型是64位類型時(shí)(double和long)牌里,他占用的Slot為index和index+1兩個(gè)。
- 姐妹屬性:在JDK1.5引入泛型之后务甥,LocalVariableTable屬性增加了一個(gè)“姐妹屬性”:LocalVariableTypeTable牡辽,這個(gè)新增的屬性結(jié)構(gòu)與LocalVariableTable非常相似,僅僅是吧記錄的字段描述符的descriptor_index替換成了字段的特征簽名(Signature)敞临,對(duì)于非泛型類型來(lái)說态辛,描述符和特征簽名能描述的信息是基本一致的,但是泛型引入后哟绊,由于描述符中反省的參數(shù)化類型被擦除掉因妙,描述符就不能準(zhǔn)確的描述泛型類型了,因此出現(xiàn)了LocalVariableTypeTable票髓。
SourceFile屬性
SourceFile屬性用于記錄生成這個(gè)Class文件的源碼文件名稱攀涵。這個(gè)屬性也是可選的,可以分別使用Javac的-g:none
或-g: source
選項(xiàng)來(lái)關(guān)閉或要求生成這項(xiàng)信息洽沟。在Java中以故,對(duì)于大多數(shù)的類來(lái)說,類名和文件名是一致的裆操,但是有一些特殊情況(如內(nèi)部類)例外怒详。如果不生成這項(xiàng)屬性,當(dāng)拋出異常時(shí)踪区,堆棧中將不會(huì)顯示出錯(cuò)代碼所屬的文件名昆烁。這個(gè)屬性是一個(gè)定長(zhǎng)的屬性,其結(jié)構(gòu)見下表缎岗。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | sourcefile_index |
- sourcefile_index數(shù)據(jù)項(xiàng):是指向常量池中CONSTANT_Utf8_info型常量的索引静尼,常量值是源碼我呢見的文件名。
ConstantValue屬性
ConstantValue屬性的作用是通知虛擬機(jī)自動(dòng)為靜態(tài)變量賦值传泊。只有被static關(guān)鍵字修飾的變量(類變量)才可以使用這項(xiàng)屬性鼠渺。
類似“int x = 123”和“static int x=123”這樣的變量定義在Java程序中是非常常見的事情,但虛擬機(jī)對(duì)這兩種變量賦值的方法和時(shí)刻都有所不同眷细。對(duì)于非static類型的變量(也就是實(shí)例變量)的賦值是在實(shí)例構(gòu)造器<init>方法中進(jìn)行的拦盹;而對(duì)于類變量,則有兩種方式可以選擇:在類構(gòu)造器<clinit>方法中或者使用ConstantValue屬性溪椎。目前Sun Javac編譯器的選擇是:如果同時(shí)使用final和static來(lái)修飾一個(gè)變量(按照習(xí)慣普舆,這里稱“常量”更貼切)恬口,并且這個(gè)變量的數(shù)據(jù)類型是基本類型或者java.lang.String的話,就生成ConstantValue屬性來(lái)進(jìn)行初始化奔害,如果這個(gè)變量沒有被final修飾楷兽,或者并非基本類型及字符串,則將會(huì)選擇在<clinit>方法中進(jìn)行初始化华临。
雖然有final關(guān)鍵字才更符合“ConstantValue”的語(yǔ)義,但虛擬機(jī)規(guī)范中并沒有強(qiáng)制要求字段必須設(shè)置了ACC_FINAL標(biāo)志端考,只要求了有ConstantValue屬性的字段必須設(shè)置ACC_STATIC標(biāo)志而已雅潭,對(duì)final關(guān)鍵字的要求是javac編譯器自己加入的限制。而對(duì)ConstantValue屬性值只能限于基本類型和String却特,不過不認(rèn)為這是什么限制扶供,因?yàn)榇藢傩缘膶傩灾抵皇且粋€(gè)常量池的索引號(hào),由于Class文件格式的常量類型中只有與基本屬性和字符串相對(duì)應(yīng)的字面量裂明,所以就算ConstantValue屬性在想支持別的類型也無(wú)能為力椿浓。ConstantValue屬性的結(jié)構(gòu)見下表。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | constantvalue_index | 1 |
- ConstantValue屬性:是一個(gè)定長(zhǎng)屬性闽晦,他的attribute_length數(shù)據(jù)項(xiàng)值必須固定為2扳碍。
- constantvalue_index數(shù)據(jù)項(xiàng):代表了常量池中一個(gè)字面量常量的引用,根據(jù)字段類型的不同仙蛉,字面量可以是CONSTANT_Long_info笋敞、CONSTANT_Float_info、CONSTANT_Double_info荠瘪、CONSTANT_Integer_info夯巷、CONSTANT_String_info常量中的一種。
InnerClasses屬性
InnerClasses屬性用于記錄內(nèi)部類與宿主類之間的關(guān)聯(lián)哀墓。如果一個(gè)類中定義了內(nèi)部類趁餐,那編譯器將會(huì)為他以及他所包含的內(nèi)部類生成InnerClasses屬性。該屬性的結(jié)構(gòu)見下表篮绰。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | number_of_class | 1 |
inner_classes_info | inner_class | number_of_classes |
- number_of_classes:代表需要記錄多少個(gè)內(nèi)部類信息后雷。
- inner_classes_info表:每一個(gè)內(nèi)部類的信息都由一個(gè)inner_classes_info表進(jìn)行描述。inner_classes_info的結(jié)構(gòu)見下表阶牍。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | inner_class_info_index | 1 |
u2 | outer_class_info_index | 1 |
u2 | inner_name_index | 1 |
u2 | inner_class_access_info | 1 |
- inner_name_index:是指向常量池中CONSTANT_Utf8_info型常量的索引喷面,代表這個(gè)內(nèi)部類的名稱,如果是匿名內(nèi)部類走孽,那么這項(xiàng)值為0.
- inner_class_access_flags:是內(nèi)部類的訪問標(biāo)志惧辈,類似于類的access_flags,他的取值范圍見下表磕瓷。
標(biāo)志名稱 | 標(biāo)志值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 內(nèi)部類是否為public |
ACC_PRIVATE | 0x0002 | 內(nèi)部類是否為private |
ACC_PROTECTED | 0x0004 | 內(nèi)部類是否為protected |
ACC_STATIC | 0x0008 | 內(nèi)部類是否為static |
ACC_FINAL | 0x0010 | 內(nèi)部類是否為final |
ACC_INTERFACE | 0x0020 | 內(nèi)部類是否為synchronized |
ACC_ABSTRACT | 0x0400 | 內(nèi)部類是否為abstract |
ACC_SYNTHETIC | 0x1000 | 內(nèi)部類是否嬪妃由用戶代碼產(chǎn)生的 |
ACC_ANNOTATION | 0x2000 | 內(nèi)部類是否是一個(gè)注解 |
ACC_ENUM | 0x4000 | 內(nèi)部類是否是一個(gè)枚舉 |
Deprecated及Synthetic屬性
Deprecated和Synthetic兩個(gè)屬性都屬于標(biāo)志類型的布爾屬性盒齿,只存在有和沒有的區(qū)別念逞,沒有屬性值的概念。屬性的結(jié)構(gòu)非常簡(jiǎn)單边翁,其中attribute_length數(shù)據(jù)項(xiàng)的值必須為0x00000000翎承,因?yàn)闆]有任何屬性值需要設(shè)置,見下表:
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
- Deprecated屬性用于表示每個(gè)類符匾、字段或者方法叨咖,已經(jīng)被程序作者定位不在推薦使用,他可以通過在代碼中使用@deprecated注釋進(jìn)行設(shè)置啊胶。
- Synthetic屬性代表此字段或者方法并不是由Java源碼直接產(chǎn)生的甸各,而是由編譯器自行添加的,在JDK 1.5之后焰坪,標(biāo)識(shí)一個(gè)類趣倾、字段或者方法是編譯器自動(dòng)產(chǎn)生的,也可以設(shè)置他們?cè)L問標(biāo)志中的ACC_SYNTHETIC標(biāo)志位某饰,其中最典型的例子就是Bridge Method儒恋。所有由非用戶代碼產(chǎn)生的類、方法及字段都應(yīng)當(dāng)至少設(shè)置Synthetic屬性和ACC_SYNTHETIC標(biāo)志位中的一項(xiàng)黔漂,唯一的例外是實(shí)例構(gòu)造器“<init>”方法和類構(gòu)造器“<clinit>”方法诫尽。
StackMapTable屬性
StackMapTable屬性在JDK 1.6發(fā)布周增加到了Class文件規(guī)范中,他是一個(gè)復(fù)雜的變長(zhǎng)屬性瘟仿,位于Code屬性的屬性表箱锐,這個(gè)屬性會(huì)在虛擬機(jī)類加載的字節(jié)碼驗(yàn)證階段被新類型檢查驗(yàn)證器(Type Checker)使用,目的在于代替以前比較消耗性能的基于數(shù)據(jù)流分析的類型推導(dǎo)驗(yàn)證器劳较。
這個(gè)類型檢查驗(yàn)證器最初來(lái)源于Sheng Liang為Java ME CLDC實(shí)現(xiàn)的字節(jié)碼驗(yàn)證器驹止。新的驗(yàn)證器在同樣能保證Class文件合法性的前提下,省略了在運(yùn)行期通過數(shù)據(jù)流分析確認(rèn)字節(jié)碼的行為邏輯合法性的步驟观蜗,而是在編譯階段將一系列的驗(yàn)證類型(Verification Types)直接記錄在Class文件之中臊恋,通過檢查這些驗(yàn)證類型代替了類型推導(dǎo)過程,從而大幅提升了字節(jié)碼驗(yàn)證的性能墓捻。這個(gè)驗(yàn)證器在JDK 1.6中首次提供抖仅,并在JDK 1.7中強(qiáng)制代替原本基于類型推斷的字節(jié)碼驗(yàn)證器。
StackMapTable屬性中包含零至多個(gè)棧映射棧(Stack Map Frames)砖第,每個(gè)棧映射幀都顯示或隱式的代表了一個(gè)字節(jié)碼偏移量撤卢,用于表示該執(zhí)行到該字節(jié)碼時(shí)局部變量表和操作數(shù)棧的驗(yàn)證類型。類型檢查驗(yàn)證器會(huì)通過檢查目標(biāo)方法的局部變量和操作數(shù)棧所需要的類型來(lái)確定一段字節(jié)碼指令是否符合邏輯約束梧兼。StackMapTable屬性的結(jié)構(gòu)見下表放吩。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | number_of_entries | 1 |
stack_map_frame | stack_map_frame_entries | number_of_entries |
《Java虛擬機(jī)規(guī)范(Java SE 7版)》明確規(guī)定:在版本號(hào)大于或等于50.0的Class文件中,如果方法的Code屬性中沒有附帶StackMapTable屬性羽杰,那就意味著他帶有一個(gè)隱式的StackMap屬性渡紫。這個(gè)StackMap屬性的作用等同于number_of_entries值為0的StackMapTable屬性到推。一個(gè)方法的Code屬性最多只能有一個(gè)StackMapTable屬性,否則將拋出ClassFormatError異常惕澎。
Signature屬性
Signature屬性在JDK 1.5發(fā)布后增加到了Class文件規(guī)范之中莉测,他是一個(gè)可選的定長(zhǎng)屬性,可以出現(xiàn)于類唧喉、屬性表和方法表結(jié)構(gòu)的屬性表中捣卤。在JDK 1.5大幅增強(qiáng)了Java語(yǔ)言的語(yǔ)法,在此之后八孝,任何類腌零、接口、初始化方法或成員的泛型簽名如果包含餓了類型變量(Type Variables)或參數(shù)化類型(Parameterized Types)唆阿,則Signature屬性會(huì)為他記錄泛型簽名信息。之所以要專門使用這樣一個(gè)屬性去記錄泛型類型锈锤,是因?yàn)镴ava語(yǔ)言的泛型采用的是擦除法實(shí)現(xiàn)的偽泛型驯鳖,在字節(jié)碼(Code屬性)中,泛型信息編譯(類型變量久免、參數(shù)化類型)之后都統(tǒng)統(tǒng)被擦除掉。使用擦除法的好處是實(shí)現(xiàn)簡(jiǎn)單(主要修改Javac編譯器,虛擬機(jī)內(nèi)部只做了很少的改動(dòng))竖螃、非常容易實(shí)現(xiàn)Backport拨扶,運(yùn)行期也能夠節(jié)省一些類型所占的內(nèi)存空間。但壞處是運(yùn)行期就無(wú)法像C#等有真泛型支持的語(yǔ)言那樣呼巴,將泛型類型與用戶定義的普通類型同等對(duì)待泽腮,例如運(yùn)行期做反射時(shí)無(wú)法獲得到泛型信息。Signature屬性就是為了彌補(bǔ)這個(gè)缺陷而增設(shè)的衣赶,現(xiàn)在Java的反射API能夠獲取泛型類型诊赊,最終的數(shù)據(jù)來(lái)源也就是這個(gè)屬性。Signature屬性的結(jié)構(gòu)見下表府瞄。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | signature_index | 1 |
其中signature_index項(xiàng)的值必須是一個(gè)對(duì)常量池的有效索引碧磅。常量池在該索引處的項(xiàng)必須是CONSTANT_Utf8_info結(jié)構(gòu),表示類簽名遵馆、方法類型簽名或字段類型簽名鲸郊。如果當(dāng)前的Signature屬性是類文件的屬性,則這個(gè)結(jié)構(gòu)表示類簽名货邓,如果當(dāng)前的Signature屬性是方法表的屬性秆撮,則這個(gè)結(jié)構(gòu)表示方法類型簽名,如果當(dāng)前Signature屬性是字段表的屬性逻恐,則這個(gè)結(jié)構(gòu)表示字段類型簽名像吻。
BootstrapMethods屬性
BootstrapMethods屬性在JDK 1.7發(fā)布后增加到了Class文件規(guī)范之中峻黍,他是一個(gè)復(fù)雜的變長(zhǎng)屬性,位于類文件的屬性表中拨匆。這個(gè)屬性用于保存invokedynamic指令引用的引導(dǎo)方法限定符姆涩。《Java虛擬機(jī)規(guī)范(Java SE 7版)》規(guī)定惭每,如果某個(gè)類文件結(jié)構(gòu)的常量池中曾經(jīng)出現(xiàn)過CONSTANT_InvokeDynamic_info類型的常量骨饿,那么這個(gè)類文件的屬性表中必須存在一個(gè)明確地BootstrapMethods屬性,另外台腥,即使CONSTANT_InvokeDynamic_info類型的常量在常量池中出現(xiàn)過多次宏赘,類文件的屬性表中最多也只能一個(gè)BootstrapMethods屬性。BootstrapMethods屬性與JSR-292中的InvokeDynamic指令和java.lang.Invoke包關(guān)系非常密切黎侈。
目前的Javac暫時(shí)無(wú)法生成InvokeDynamic指令和BootstrapMethods屬性察署,必須通過一些非常規(guī)的手段才能使用到他們,也許在不久的將來(lái)峻汉,等JSR-292更加成熟一些贴汪,這種狀況就會(huì)改變。BootstrapMethods屬性的結(jié)構(gòu)見下表休吠。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | attribute_name_index | 1 |
u4 | attribute_length | 1 |
u2 | num_bootstrap_methods | 1 |
bootstrap_method | bootstrap_methods | num_bootstrap_methods |
- num_bootstrap_methods:項(xiàng)的值給出了bootstrap_methods[]數(shù)組中的引導(dǎo)方法限定符的數(shù)量扳埂。
- bootstrap_methods[]數(shù)組:的每個(gè)成員包含了一個(gè)指向常量池CONSTANT_MethodHandle結(jié)構(gòu)的索引值,他代表了一個(gè)引導(dǎo)方法瘤礁,還包含了這個(gè)引導(dǎo)方法靜態(tài)參數(shù)的序列(可能為空)阳懂。
- bootstrap_method:結(jié)構(gòu)見下表。
類型 | 名稱 | 數(shù)量 |
---|---|---|
u2 | bootstrap_method_ref | 1 |
u2 | num_bootstrap_arguments | 1 |
u2 | bootstrap_arguments | num_bootstrap_arguments |
- bootstrap_method_ref:bootstrap_method_ref項(xiàng)的值必須是一個(gè)對(duì)常量池的有效索引柜思。常量池在該索引處的值必須是一個(gè)CONSTANT_MethodHandle_info結(jié)構(gòu)岩调。
- num_bootstrap_arguments:num_bootstrap_arguments項(xiàng)的值給出了bootstrap_arguments[]數(shù)組成員的數(shù)量。
- bootstrap_arguments[]:bootstrap_arguments[]數(shù)組的每個(gè)成員必須是一個(gè)對(duì)常量池的有效索引酝蜒。常量池在該索引處必須是下列結(jié)構(gòu)之一:CONSTANT_String_info誊辉、CONSTANT_Class_info、CONSTANT_Integer_info亡脑、CONSTANT_Long_info堕澄、CONSTANT_Float_info、CONSTANT_Double_info霉咨、CONSTANT_MethodHandle_info或CONSTANT_MethodType_info蛙紫。
4.3 字節(jié)碼指令
Java虛擬機(jī)的指令由一個(gè)字節(jié)長(zhǎng)度的、代表著某種特定操作含義的數(shù)字(稱為操作碼途戒,Opcode)以及跟隨其后的零至多個(gè)代表此操作所需參數(shù)(稱為操作數(shù)坑傅,Operands)而構(gòu)成。由于Java虛擬機(jī)采用面向操作數(shù)棧而不是寄存器的架構(gòu)喷斋,所以大多數(shù)的指令都不包含操作數(shù)唁毒,只有一個(gè)操作碼蒜茴。
操作碼總數(shù):Java虛擬機(jī)操作碼的長(zhǎng)度為一個(gè)字節(jié),這意味著指令集的操作碼總數(shù)不可能超過256條
放棄操作數(shù)對(duì)齊:由于Class文件格式放棄了編譯后代碼的操作數(shù)長(zhǎng)度對(duì)齊浆西,這就意味著虛擬機(jī)處理那些超過一個(gè)字節(jié)數(shù)據(jù)的時(shí)候粉私,不得不在運(yùn)行時(shí)從字節(jié)中重建出具體數(shù)據(jù)的結(jié)構(gòu),如果要將一個(gè)16位長(zhǎng)度的無(wú)符號(hào)整數(shù)使用兩個(gè)無(wú)符號(hào)字節(jié)存儲(chǔ)起來(lái)(將它們命名為byte1和byte2)近零,那他們的值應(yīng)該是這樣的:
(byte1 << 8) | byte2
4.3.1 字節(jié)碼與數(shù)據(jù)類型
- 大多數(shù)的指令都包含了其操作所對(duì)應(yīng)的數(shù)據(jù)類型信息诺核,iload指令用于從局部變量表中加載int型的數(shù)據(jù)到操作數(shù)棧中,而fload指令加載的則是float類型的數(shù)據(jù)久信。
- 大部分與數(shù)據(jù)類型相關(guān)的字節(jié)碼指令窖杀,他們的操作碼助記符中都有特殊的字符來(lái)表明專門為哪種數(shù)據(jù)類型服務(wù):i代表對(duì)int類型的數(shù)據(jù)操作,l代表long裙士,s代表short入客,b代表byte,c代表char腿椎,f代表float痊项,d代表double,a代表reference酥诽。
- 有一些指令的助記符中沒有明確地指明操作類型的字母,如arraylength指令皱埠,他沒有代表數(shù)據(jù)類型的特殊字符肮帐,但操作數(shù)永遠(yuǎn)只能是一個(gè)數(shù)組類型的對(duì)象。
- 還有一些指令如無(wú)條件跳轉(zhuǎn)指令goto則是與數(shù)據(jù)類型無(wú)關(guān)的边器。
- 由于Java虛擬機(jī)的操作碼最多只有256個(gè)训枢,Java虛擬機(jī)的指令被設(shè)計(jì)成非完全獨(dú)立的(Java虛擬機(jī)規(guī)范中把這種特性稱為“Not Orthogonal”,即并非每種數(shù)據(jù)類型和每一種操作都有對(duì)應(yīng)的指令)忘巧。
- 大部分的指令都沒有支持整數(shù)類型byte恒界、char和short,甚至沒有任何指令支持boolean類型砚嘴。編譯器會(huì)在編譯器或運(yùn)行期將byte和short類型的數(shù)據(jù)帶符號(hào)擴(kuò)展(Sign-Extend)為相應(yīng)的int類型數(shù)據(jù)十酣,將boolean和char類型數(shù)據(jù)零位擴(kuò)展(Zero-Extend)為相應(yīng)的int類型數(shù)據(jù)。與之類似际长,在處理boolean耸采、byte、short和char類型的數(shù)組時(shí)工育,也會(huì)轉(zhuǎn)換為使用對(duì)應(yīng)的int類型的字節(jié)碼指令來(lái)處理虾宇。因此,大多數(shù)對(duì)于boolean如绸、byte嘱朽、short和char類型數(shù)據(jù)的操作旭贬,實(shí)際上都是使用相應(yīng)的int類型作為運(yùn)算類型(Computational Type)。
4.3.2 加載和存儲(chǔ)指令
加載和存儲(chǔ)指令用于將數(shù)據(jù)在棧幀中的局部變量表和操作數(shù)棧之間來(lái)回傳輸搪泳,這類指令包括如下內(nèi)容稀轨。
- 將一個(gè)局部變量加載到操作棧:
iload、iload_<n>森书、lload靶端、lload_<n>、fload凛膏、fload_<n>杨名、dload、dload_<n>猖毫、aload台谍、aload_<n>
- 將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量表:
istore、istore_<n>吁断、lstore趁蕊、lstore_<n>、fstore仔役、fstore_<n>掷伙、dstore、dstore_<n>又兵、astore任柜、astore_<n>
- 將一個(gè)常量加載到操作數(shù)棧:
bipush、sipush沛厨、ldc宙地、ldc_w、ldc2_w逆皮、aconst_null宅粥、iconst_ml、iconst_<i>电谣、lconst_<l>秽梅、fconst_<f>、dconst_<d>
- 擴(kuò)充局部變量表的訪問索引的指令:
wide
- 以尖括號(hào)結(jié)尾的(例如iload_<n>)這些指令助記符實(shí)際上是代表了一組指令(例如iload_<n>剿牺,他代表了iload_0风纠、iload_1、iload_2和iload_3這幾條指令)牢贸。這幾組指令都是某個(gè)帶有一個(gè)操作數(shù)的通用指令的特殊形式竹观。對(duì)于這若干組特殊指令來(lái)說,他們省略掉了顯示的操作數(shù),不需要進(jìn)行取操作數(shù)的動(dòng)作臭增,實(shí)際上操作數(shù)就隱含在指令中懂酱。除了這點(diǎn)之外,他們的語(yǔ)義與原生的通用指令完全一致(例如iload_0的語(yǔ)義與操作數(shù)為0時(shí)的iload指令語(yǔ)義完全一致)誊抛。
4.3.3 運(yùn)算指令
運(yùn)算或算術(shù)指令用于對(duì)兩個(gè)操作數(shù)棧上的值進(jìn)行某種特定運(yùn)算列牺,并把結(jié)果重新存入到操作棧頂。大體上算術(shù)指令可以分為兩種:對(duì)整型數(shù)據(jù)進(jìn)行運(yùn)算的指令與對(duì)浮點(diǎn)型數(shù)據(jù)進(jìn)行運(yùn)算的指令拗窃。由于沒有直接支持byte瞎领、short、char和boolean類型的算術(shù)指令随夸,對(duì)于這類數(shù)據(jù)的運(yùn)算九默,應(yīng)使用操作int類型的指令代替。整數(shù)與浮點(diǎn)數(shù)的算術(shù)指令在溢出和被零除的時(shí)候也有各自不同的行為表現(xiàn)宾毒,所有的算術(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样傍。
整數(shù)運(yùn)算
- 在處理整型數(shù)據(jù)時(shí)横缔,只有除法指令(idiv和ldiv)以及求余指令(irem和lrem)中當(dāng)出現(xiàn)除數(shù)為零時(shí)會(huì)導(dǎo)致虛擬機(jī)拋出ArithmeticException異常,其余任何整型數(shù)運(yùn)算場(chǎng)景都不應(yīng)該拋出運(yùn)行時(shí)異常衫哥。
- 對(duì)long類型數(shù)值進(jìn)行比較時(shí)茎刚,虛擬機(jī)采用帶符號(hào)的比較方式,而
浮點(diǎn)數(shù)運(yùn)算
- 虛擬機(jī)在處理浮點(diǎn)數(shù)時(shí)必須嚴(yán)格遵循IEEE 754規(guī)范中所規(guī)定的行為和限制撤逢。也就是說膛锭,Java虛擬機(jī)必須完全支持IEEE 754中定義的非正規(guī)浮點(diǎn)數(shù)值(Denormalized Floating-Point Numbers)和逐級(jí)下溢(Gradual Underflow)的運(yùn)算規(guī)則。
- 所有的運(yùn)算結(jié)果都必須舍入到適當(dāng)?shù)木任萌伲蔷_的結(jié)果必須舍入為可被表示的最接近的精確值初狰,如果有兩種可表示的形式與該值一樣接近,將優(yōu)先選擇最低有效位為零的互例。
- Java虛擬機(jī)在處理浮點(diǎn)數(shù)運(yùn)算時(shí)奢入,不會(huì)拋出任何運(yùn)行時(shí)異常(這里所講的是Java語(yǔ)言中的異常,勿與IEEE 754規(guī)范中的浮點(diǎn)異诚边叮互相混淆腥光,IEEE 754的浮點(diǎn)異常是一種運(yùn)算信號(hào)),當(dāng)一個(gè)操作產(chǎn)生溢出時(shí)糊秆,將會(huì)使用有符號(hào)的無(wú)窮大來(lái)表示武福,如果某個(gè)操作結(jié)果沒有明確的數(shù)學(xué)定義的話,將會(huì)使用NaN值來(lái)表示痘番。所有使用NaN值作為操作數(shù)的算術(shù)操作捉片,結(jié)果都會(huì)返回NaN平痰。
- 對(duì)浮點(diǎn)數(shù)值進(jìn)行比較時(shí)(dcmpg、dcmpl界睁、fcmpg觉增、fcmpl),虛擬機(jī)會(huì)采用IEEE 754規(guī)范所定義的無(wú)信號(hào)比較(Nonsignaling Comparisons)方式翻斟。
4.3.4 類型轉(zhuǎn)換指令
類型轉(zhuǎn)換指令可以將兩種不同的數(shù)值類型進(jìn)行相互轉(zhuǎn)換逾礁,JVM直接支持小范圍類型向大范圍類型的安全轉(zhuǎn)換,而處理大范圍類型到小范圍類型的窄化類型轉(zhuǎn)換則需要顯示地使用轉(zhuǎn)換指令來(lái)完成访惜,這些指令包括:i2b嘹履、i2c、i2s债热、l2i砾嫉、f2i、f2l窒篱、d2i焕刮、d2l和d2f。
窄化類型轉(zhuǎn)換會(huì)導(dǎo)致結(jié)果產(chǎn)生不同的正負(fù)號(hào)墙杯、不同的數(shù)量級(jí)配并、數(shù)值精度丟失的情況,但永遠(yuǎn)不可能拋出運(yùn)行時(shí)異常高镐。
4.3.5 對(duì)象創(chuàng)建與訪問指令
類實(shí)例與數(shù)組都屬于對(duì)象溉旋,但是其創(chuàng)建與操作使用了不同的字節(jié)碼指令,指令如下:
- 創(chuàng)建類實(shí)例:new
- 創(chuàng)建數(shù)組:newarray, anewarray, multianewarray
- 訪問類字段(static字段)和實(shí)例字段:getfield, putfield, getstatic, putstatic
- 把一個(gè)數(shù)組元素加載到操作數(shù)棧:baload, caload, saload, iaload, laload, faload, etc.
- 把一個(gè)操作數(shù)棧的值存儲(chǔ)到數(shù)組元素中:bastore, castore, sastore, iastore, etc.
- 取數(shù)組長(zhǎng)度:arraylength
- 檢查類實(shí)例類型:instanceof, checkcast
4.3.6 操作數(shù)棧管理指令
如同操作一個(gè)普通數(shù)據(jù)結(jié)構(gòu)中的堆棧那樣嫉髓,Java虛擬機(jī)提供了一些用于直接操作數(shù)棧的指令观腊,包括:
- 將操作數(shù)棧的棧頂一個(gè)或兩個(gè)元素出棧:pop、pop2
- 復(fù)制棧頂一個(gè)或兩個(gè)數(shù)值并將復(fù)制值或雙份的復(fù)制值重新壓入棧頂:dup算行、dup2梧油、dup_x1、dup2_x1州邢、dup_x2儡陨、dup2_x2
- 將棧最頂端的兩個(gè)數(shù)值互換:swap
4.3.7 控制轉(zhuǎn)移指令
控制轉(zhuǎn)移指令可以讓Java虛擬機(jī)有條件或無(wú)條件的從指定的位置指令而不是控制轉(zhuǎn)移指令的下一條指令繼續(xù)執(zhí)行程序,從概念模型上理解偷霉,可以認(rèn)為控制轉(zhuǎn)移指令就是在有條件或無(wú)條件的修改PC寄存器的值『稚福控制轉(zhuǎn)移指令如下类少。
- 條件分支:ifeq、iflt渔扎、ifle硫狞、ifne、ifgt、ifge残吩、ifnull财忽、ifnonnull、if_icmpeq泣侮、if_icmpne即彪、if_icmplt、if_icmpgt活尊、if_icmple隶校、if_icmpge、if_acmpeq和if_acmpne蛹锰。
- 復(fù)合條件分支:tableswitch深胳、lookupswitch。
- 無(wú)條件分支:goto铜犬、goto_w舞终、jsr、jsr_w癣猾、ret敛劝。
int、reference煎谍、null指令集:在Java虛擬機(jī)中有專門的指令集用來(lái)處理int和reference類型的條件分支比較操作攘蔽;為了可以無(wú)需明顯標(biāo)識(shí)一個(gè)實(shí)體值是否null,也有專門的指令用來(lái)檢測(cè)null值呐粘。
轉(zhuǎn)化成int類型:與算術(shù)運(yùn)算時(shí)的規(guī)則一致满俗,對(duì)于boolean類型、byte類型作岖、char類型和short類型的條件分支比較操作唆垃,則會(huì)先執(zhí)行相應(yīng)類型的比較運(yùn)算指令(dcmpg、dcmpl痘儡、fcmpg辕万、fcmpl、lcmp)沉删,運(yùn)算指令會(huì)返回一個(gè)整形值到操作數(shù)棧中渐尿,隨后再執(zhí)行int類型的條件分支比較操作來(lái)完成整個(gè)分支跳轉(zhuǎn)。由于各種類型的比較最終都會(huì)轉(zhuǎn)化為int類型的比較操作矾瑰,int類型比較是否方便完善就顯得尤為重要砖茸,所以Java虛擬機(jī)提供的int類型的條件分支指令是最為豐富和強(qiáng)大的。
4.3.8 方法調(diào)用和返回指令
方法調(diào)用指令與數(shù)據(jù)類型無(wú)關(guān)殴穴,而方法返回指令是根據(jù)返回值的類型區(qū)分的凉夯,包括ireturn(當(dāng)返回值是boolean货葬、byte、char劲够、short和int類型時(shí)使用)震桶、lreturn、freturn征绎、dreturn和areturn蹲姐;另外還有一條return指令供聲明為void的方法、實(shí)例初始化方法以及類和接口的類初始化方法使用炒瘸。以下列舉了5條用于方法調(diào)用的指令:
- invokevirtual——指令用于調(diào)用對(duì)象的實(shí)例方法淤堵,根據(jù)對(duì)象的實(shí)際類型進(jìn)行分派(虛方法分派),這也是Java語(yǔ)言中最常見的方法分派方式顷扩。
- invokeinterface——指令用于調(diào)用接口方法拐邪,他會(huì)在運(yùn)行時(shí)搜索一個(gè)實(shí)現(xiàn)了這個(gè)接口方法的對(duì)象,找出適合的方法進(jìn)行調(diào)用隘截。
- invokespecial——指令用于調(diào)用一些需要特殊處理的實(shí)例方法扎阶,包括實(shí)例初始化方法、私有方法和父類方法婶芭。
- invokestatic——指令用于調(diào)用類方法(static方法)东臀。
- invokedynamic——指令用于運(yùn)算時(shí)動(dòng)態(tài)解析出調(diào)用點(diǎn)限定符所引用的方法,并執(zhí)行該方法犀农,前面4條調(diào)用指令的分派邏輯都固化在Java虛擬機(jī)內(nèi)部惰赋,而invokedynamic指令的分派邏輯是由用戶所設(shè)定的引導(dǎo)方法決定的。
4.3.9 異常處理指令
- 在Java程序中顯示拋出異常的操作(throw 語(yǔ)句)都由athrow指令來(lái)實(shí)現(xiàn)
- 除了用throw語(yǔ)句顯式拋出異常情況之外呵哨,Java虛擬機(jī)規(guī)范還規(guī)定了許多運(yùn)行時(shí)異常會(huì)在其他Java虛擬機(jī)指令檢測(cè)到異常狀況時(shí)自動(dòng)拋出赁濒。
- 在Java虛擬機(jī)中,處理異常(catch語(yǔ)句)不是由字節(jié)碼指令來(lái)實(shí)現(xiàn)的(很久之前曾經(jīng)使用jsr和ret指令來(lái)實(shí)現(xiàn)孟害,現(xiàn)在已經(jīng)不用了)拒炎,而是采用異常表來(lái)完成的。
4.3.10 同步指令
Java虛擬機(jī)可以支持方法級(jí)的同步和方法內(nèi)部一段指令序列的同步挨务,這兩種同步結(jié)構(gòu)是使用管程(Monitor)來(lái)支持的击你。
方法級(jí)的同步
- 方法級(jí)的同步是隱式的,即無(wú)需通過字節(jié)碼指令來(lái)控制谎柄,他實(shí)現(xiàn)在方法調(diào)用和返回操作之中丁侄。
- 虛擬機(jī)可以從方法常量池的方法表結(jié)構(gòu)中的ACC_SYNCHRONIZED訪問標(biāo)志得知一個(gè)方法是否聲明為同步方法。
- 當(dāng)方法調(diào)用時(shí)朝巫,調(diào)用指令將會(huì)檢查方法的ACC_SYNCHRONIZED訪問標(biāo)志是否被設(shè)置鸿摇,如果設(shè)置了,執(zhí)行線程就要求先成功持有管程捍歪,然后才能執(zhí)行方法户辱,最后當(dāng)方法完成(無(wú)論是正常完成還是非正常完成)時(shí)釋放管程。
- 在方法執(zhí)行期間糙臼,執(zhí)行線程持有了管程庐镐,其他任何線程都無(wú)法再獲取到同一個(gè)管程。如果一個(gè)同步方法執(zhí)行期間拋出了異常变逃,并且在方法內(nèi)部無(wú)法處理此異常必逆,那么這個(gè)同步方法所持有的管程將在異常拋到同步方法之外時(shí)自動(dòng)釋放。
同步一段指令集
- 同步一段指令集通常是由Java語(yǔ)言中的synchronized語(yǔ)句塊來(lái)表示的揽乱。
- Java虛擬機(jī)的指令集中有monitorenter和monitorexit兩條指令來(lái)支持synchronized關(guān)鍵字的語(yǔ)義名眉,正確實(shí)現(xiàn)synchronized關(guān)鍵字需要Javac編譯器與Java虛擬機(jī)兩者共同協(xié)作支持。
- 編譯器必須確保無(wú)論方法通過何種方式完成凰棉,方法中調(diào)用過的每條monitorenter指令都必須執(zhí)行其對(duì)應(yīng)的monitorexit指令损拢,而無(wú)論這個(gè)方法是正常結(jié)束還是異常結(jié)束。
- 為了保證在方法異常完成時(shí)monitorenter和monoitorexit指令依然剋有正確配對(duì)執(zhí)行撒犀,編譯器會(huì)自動(dòng)產(chǎn)生一個(gè)異常處理器福压,這個(gè)異常處理器聲明可處理所有的異常,他的目的就是用來(lái)執(zhí)行monitorexit指令或舞。
信號(hào)量與管程
管程:管程可以看做一個(gè)軟件模塊荆姆,它是將共享的變量和對(duì)于這些共享變量的操作封裝起來(lái),形成一個(gè)具有一定接口的功能模塊映凳,進(jìn)程可以調(diào)用管程來(lái)實(shí)現(xiàn)進(jìn)程級(jí)別的并發(fā)控制胆筒。進(jìn)程只能互斥得使用管程,即當(dāng)一個(gè)進(jìn)程使用管程時(shí)诈豌,另一個(gè)進(jìn)程必須等待仆救。當(dāng)一個(gè)進(jìn)程使用完管程后,它必須釋放管程并喚醒等待管程的某一個(gè)進(jìn)程队询。在管程入口處的等待隊(duì)列稱為入口等待隊(duì)列派桩,由于進(jìn)程會(huì)執(zhí)行喚醒操作,因此可能有多個(gè)等待使用管程的隊(duì)列蚌斩,這樣的隊(duì)列稱為緊急隊(duì)列铆惑,它的優(yōu)先級(jí)高于等待隊(duì)列。
信號(hào)量:信號(hào)量是一種抽象數(shù)據(jù)類型送膳,由一個(gè)整形 (sem)變量和兩個(gè)原子操作組成:
- P():sem減1员魏,如果sem<0等待,否則繼續(xù)叠聋;
- V():sem加1撕阎,如果sem<=0,說明當(dāng)前有等著的進(jìn)程碌补,喚醒掛在信號(hào)量上的等待進(jìn)程虏束,可以是一個(gè)或多個(gè) 棉饶。
4.4 公有設(shè)計(jì)和私有實(shí)現(xiàn)
Java虛擬機(jī)規(guī)范描繪了Java虛擬機(jī)應(yīng)有的共同程序存儲(chǔ)格式:Class文件格式以及字節(jié)碼指令集。這些內(nèi)容與硬件镇匀、操作系統(tǒng)及具體的Java虛擬機(jī)實(shí)現(xiàn)之間是完全獨(dú)立的照藻。
Java虛擬機(jī)實(shí)現(xiàn)必須能夠讀取Class文件并精確實(shí)現(xiàn)包含在其中的Java虛擬機(jī)代碼的語(yǔ)義,一個(gè)優(yōu)秀的虛擬機(jī)實(shí)現(xiàn)汗侵,在滿足虛擬機(jī)規(guī)范的約束下對(duì)具體實(shí)現(xiàn)做出修改和優(yōu)化也是完全可行的幸缕,并且虛擬機(jī)規(guī)范中明確鼓勵(lì)實(shí)現(xiàn)者這樣做。只要優(yōu)化后Class文件依然可以被正確讀取晰韵,并且包含在其中的語(yǔ)義能得到完整的保持发乔,那實(shí)現(xiàn)者就可以選擇任何方式去實(shí)現(xiàn)這些語(yǔ)義。
虛擬機(jī)實(shí)現(xiàn)者可以使用這種伸縮性來(lái)讓Java虛擬機(jī)獲得更高的性能雪猪、更低的內(nèi)存消耗或者更好的可移植性栏尚,選擇哪種特性取決于Java虛擬機(jī)實(shí)現(xiàn)的目標(biāo)和關(guān)注點(diǎn)是什么。虛擬機(jī)實(shí)現(xiàn)的方式主要有以下兩種:
- 將輸入的Java虛擬機(jī)代碼在加載或執(zhí)行時(shí)翻譯成另外一種虛擬機(jī)的指令集只恨。
- 將輸入的Java虛擬機(jī)代碼在加載或執(zhí)行時(shí)翻譯成宿主CPU的本地指令集(即JIT代碼生成技術(shù))抵栈。