Java在剛剛誕生之初曾經(jīng)提出過(guò)一個(gè)非常著名的口號(hào):“一次編譯奴迅,導(dǎo)出執(zhí)行”,將java文件編譯為class文件,然后由JVM來(lái)處理平臺(tái)的差異性梢灭,這個(gè)特性也使其他的語(yǔ)言能夠在JVM上運(yùn)行。
Class類(lèi)文件的結(jié)構(gòu)
占用大小 | 字段描述 | 數(shù)量 |
---|---|---|
4bit | magic:魔數(shù)蒸其,用于標(biāo)識(shí)文件類(lèi)型敏释,對(duì)于java來(lái)說(shuō)是0xCAFEBABE | 1 |
2bit | minor_version:次版本號(hào) | 1 |
2bit | major_version:主版本號(hào) | 1 |
2bit | constant_pool_count:常量池大小,從1開(kāi)始而不是0摸袁。當(dāng)這個(gè)值為0時(shí)钥顽,表示后面沒(méi)有常量 | 1 |
不定 | constant_pool:#常量池 | constant_pool_count-1 |
2bit | access_flags:訪問(wèn)標(biāo)志,標(biāo)識(shí)這個(gè)class是類(lèi)還是接口靠汁、public蜂大、abstract、final等 | 1 |
2bit | this_class:類(lèi)索引 #類(lèi)索引查找全限定名的過(guò)程 | 1 |
2bit | super_class:父類(lèi)索引 | 1 |
2bit | interfaces_count:接口計(jì)數(shù)器 | 1 |
每個(gè)2bit | interfaces:接口索引集合 | interfaces_count |
2bit | fields_count:字段的數(shù)量 | 1 |
不定 | fields:#字段表 | fields_count |
2bit | methods_count:方法數(shù)量 | 1 |
不定 | methods:#方法表 | methods_count |
2bit | attributes_count:屬性數(shù)量 | 1 |
不定 | attrbutes:#屬性表 | attributes_count |
常量池
主要存放兩大類(lèi)常量:
- 字面量(Literal):類(lèi)似于java的常量
- 符號(hào)引用(Symbolic References):屬于編譯原理方面的概念蝶怔,主要包括類(lèi)和接口的權(quán)限定名(Fully Qualified Name)县爬、字段名稱(chēng)和描述符(Descriptor)、方法名稱(chēng)和描述符添谊。
類(lèi)索引查找全限定名的過(guò)程
字段表
字段表集合中不會(huì)列出從超類(lèi)或者父接口中繼承而來(lái)的字段
- access_flags:字段標(biāo)識(shí)符财喳,public、private斩狱、protected耳高、static、final所踊、volatile泌枪、transient等
- name_index:字段的簡(jiǎn)單名稱(chēng)
- descriptor_index:字段或方法的描述符
- attributes_count:屬性數(shù)量
- attributes:#屬性表
字段訪問(wèn)標(biāo)志:
方法表
屬性表
Class文件、字段表秕岛、方法表都可以攜帶自己的屬性表數(shù)據(jù)集合碌燕。與Class文件中其他的數(shù)據(jù)項(xiàng)目要求嚴(yán)格不同误证,屬性表集合的限制相對(duì)寬松。在java虛擬機(jī)規(guī)范1.7版本中定義了21項(xiàng)屬性修壕。
1. Code屬性
方法體內(nèi)java代碼編譯后生成的字節(jié)碼指令存儲(chǔ)在Code屬性?xún)?nèi)愈捅,Code屬性表的結(jié)構(gòu):
- attribute_name_index:是一項(xiàng)指向CONSTANT_Utf8_info型常量的索引,常量為“Code”
- attribute_length:屬性值的長(zhǎng)度
- max_stack:操作數(shù)棧(Operand Stacks)深度的最大值
- max_locals:局部變量表所需的存儲(chǔ)空間
- code_length:字節(jié)碼長(zhǎng)度
- code:用于存儲(chǔ)字節(jié)碼指令的一系列字節(jié)流慈鸠,每個(gè)指令就是一個(gè)1bit的單字節(jié) 一字節(jié) => 指令 => 動(dòng)作 (linux命令行蓝谨、匯編)
- exception_table_length:異常表長(zhǎng)度
-
exception_table:異常表
- start_pc:開(kāi)始行
- end_pc:結(jié)束行
- handler_pc:處理異常行
- catch_type:當(dāng)catch_type類(lèi)型或其子類(lèi)型的異常發(fā)生時(shí),轉(zhuǎn)到handler_pc
2. Exceptions屬性
Exceptions屬性的作用是列舉處方法中可能拋出的受檢異常(Checked Exceptions)
- number_of_exceptions:受檢異常數(shù)
- exception_index_table:是一個(gè)指向CONSTANT_Class_info型常量的索引青团,代表異常類(lèi)型
3. LineNumberTable屬性
LineNumberTable屬性用于描述java源碼行號(hào)與字節(jié)碼號(hào)碼之間的對(duì)應(yīng)關(guān)系譬巫。它不是運(yùn)行時(shí)必需的屬性,可以不生成督笆。不生成時(shí)芦昔,拋出異常堆棧中不會(huì)顯示出錯(cuò)的行號(hào)
line_number_table是一個(gè)數(shù)量為line_number_table_length,類(lèi)型為line_number_info的集合娃肿,line_number_info表包括了start_pc和line_number兩個(gè)2bit的數(shù)據(jù)項(xiàng)烟零,前者是字節(jié)碼行號(hào),后者是java源碼行號(hào)
4. LocalVariableTable屬性
LocalVariableTable屬性用于描述幀棧中局部變量表中的變量與java源碼中定義的變量之間的關(guān)系咸作,也不是必需的锨阿,可以不生成。不生成時(shí)记罚,其他人引用這個(gè)方法墅诡,所有的參數(shù)名稱(chēng)都將會(huì)丟失
local_variable_info項(xiàng)目代表了一個(gè)棧幀與源碼中的局部變量的關(guān)聯(lián)
- start_pc:這個(gè)局部變量的生命周期開(kāi)始的字節(jié)碼偏移量
- length:作用范圍覆蓋的長(zhǎng)度
- name_index:局部變量名稱(chēng)
- descriptor_index:局部變量的描述符
- index:局部變量在棧幀局部變量表中Slot的位置
5. SourceFile屬性
SourceFile屬性用于記錄生成這個(gè)Class文件的源碼文件名稱(chēng),也不是必需的桐智,可以不生成末早。不生成時(shí),拋出異常堆棧中將不會(huì)顯示出錯(cuò)代碼所屬的文件名
6. ConstantValue屬性
ConstantValue屬性的作用是通知虛擬機(jī)自動(dòng)為靜態(tài)變量賦值
7. InnerClasses屬性
InnerClasses屬性用于記錄內(nèi)部類(lèi)與宿主類(lèi)之間的關(guān)聯(lián)
8. Deprecated及Synthetic屬性
這兩個(gè)屬性都是布爾屬性说庭。Deprecated代表這個(gè)類(lèi)然磷、字段或方法已經(jīng)過(guò)時(shí),不再推薦使用刊驴。Synthetic代表此字段或者方法不是由java源碼直接產(chǎn)生的姿搜,而是由編譯器自行添加的。
9. StackMapTable屬性
StackMapTable屬性會(huì)在虛擬機(jī)類(lèi)加載的字節(jié)碼驗(yàn)證階段被新類(lèi)型檢查驗(yàn)證器(Type Checker)使用目的在于代替以前比較消耗性能的基于數(shù)據(jù)流分析的類(lèi)型推導(dǎo)驗(yàn)證器
10. Signature屬性
Signature屬性用于記錄泛型簽名信息
11. BootstrapMethods屬性
BootstrapMethods屬性用于保存invokedynamic指令引用的引導(dǎo)方法限定符
bootstrap_method屬性結(jié)構(gòu)
- bootstrap_method_ref:必須是一個(gè)對(duì)常量池中CONSTANT_MethodHandle_info結(jié)構(gòu)的有效索引
- num_bootstrap_arguments:bootstrap_arguments的數(shù)量
- bootstrap_arguments[]:必須是一個(gè)對(duì)常量池的有效索引
字節(jié)碼指令簡(jiǎn)介
java虛擬機(jī)的指令由一個(gè)字節(jié)長(zhǎng)度的捆憎、代表著某種特定操作含義的數(shù)字(操作碼Opcode)以及跟隨其后的0至多個(gè)代表此操作所需參數(shù)(操作數(shù)Operands)而構(gòu)成舅柜。由于java虛擬機(jī)采用面向操作數(shù)棧而不是寄存器的架構(gòu),所以大多數(shù)的指令都不包含操作數(shù)躲惰,只有一個(gè)操作碼
字節(jié)碼與數(shù)據(jù)類(lèi)型
- l代表long
- s代表short
- b代表byte
- c代表char
- f代表float
- d代表double
- a代表reference
在java虛擬機(jī)中指令集不是完全獨(dú)立的(Not Orthogonal)致份,即不是每種數(shù)據(jù)類(lèi)型和每一種操作都有對(duì)應(yīng)的指令,有一些單獨(dú)的指令可以在必要的時(shí)候用在將一些不支持的類(lèi)型轉(zhuǎn)換為可被支持的類(lèi)型
加載和存儲(chǔ)命令
加載和存儲(chǔ)指令用于將數(shù)據(jù)在幀棧中的局部變量表和操作數(shù)棧之間來(lá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ù)加載到操作數(shù)棧:bipush弄企、sipush超燃、ldc、ldc_w拘领、ldc2_w意乓、aconst_null、iconst_m1约素、iconst_<\i>届良、lconst_<l>、fconst_<\f>圣猎、dconst_<\d>
- 擴(kuò)充局部變量表的訪問(wèn)索引的指令:wide
上面帶尖括號(hào)的指令實(shí)際上是代表的一組指令士葫,如iload_0、iload_1送悔、iload_2和iload_3慢显。這些指令把操作數(shù)隱含在名稱(chēng)內(nèi),不需要進(jìn)行取操作數(shù)的動(dòng)作
運(yùn)算指令
運(yùn)算或算術(shù)指令用于對(duì)兩個(gè)操作數(shù)棧上的值進(jìn)行某種特定運(yùn)算欠啤,并把結(jié)果重新存入到操作棧頂鳍怨,可分為整型數(shù)據(jù)和浮點(diǎn)型數(shù)據(jù)指令。byte跪妥、short鞋喇、char和boolean類(lèi)型的算術(shù)指令使用int類(lèi)型的指令代替
- 加法指令: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
類(lèi)型轉(zhuǎn)換指令
類(lèi)型轉(zhuǎn)換指令可以將兩種不同的數(shù)值類(lèi)型進(jìn)行相互轉(zhuǎn)換喇勋,一般用于實(shí)現(xiàn)用戶代碼中的顯示類(lèi)型轉(zhuǎn)換操作,或者處理字節(jié)碼指令集中數(shù)據(jù)類(lèi)型相關(guān)指令無(wú)法與數(shù)據(jù)類(lèi)型一一對(duì)應(yīng)的問(wèn)題
- 寬化類(lèi)型轉(zhuǎn)換(Widening Numeric Conversions偎行,即小范圍類(lèi)型向大范圍類(lèi)型的安全轉(zhuǎn)換):轉(zhuǎn)換時(shí)無(wú)需顯式的轉(zhuǎn)換指令
- 窄化類(lèi)型轉(zhuǎn)換(Narrowing Numeric Conversions):必需顯式地使用轉(zhuǎn)換指令川背。i2b、i2c蛤袒、i2s熄云、l2i、f2i妙真、f2l缴允、d2i、d2l珍德、d2f练般,轉(zhuǎn)換時(shí)可能導(dǎo)致數(shù)值的進(jìn)度丟失
對(duì)象創(chuàng)建與訪問(wèn)指令
- 創(chuàng)建類(lèi)實(shí)例的指令:new
- 創(chuàng)建數(shù)組的指令:newarray矗漾、anewarray、multianewarray
- 訪問(wèn)類(lèi)字段和實(shí)例字段的實(shí)例:getfield薄料、putfield敞贡、getstatic、putstatic
- 把一個(gè)數(shù)組元素加載到操作數(shù)棧的指令:baload摄职、caload誊役、saload、iaload谷市、laload蛔垢、faload、daload歌懒、aaload
- 將一個(gè)操作數(shù)棧的值存儲(chǔ)到數(shù)組元素中的指令:bastore啦桌、castore溯壶、sastore及皂、iastore、fasotre且改、dastore验烧、aastore
- 取數(shù)組長(zhǎng)度的指令:arraylength
- 檢查類(lèi)實(shí)例類(lèi)型的指令:instanceof、checkcast
操作數(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
控制轉(zhuǎn)移指令
- 條件分支:fieq礼烈、iflt弧满、ifle、ifne此熬、ifgt庭呜、ifge、ifnull犀忱、ifnonnull募谎、if_icmpeq、if_icmpne阴汇、if_icmplt数冬、if_icmpgt、if_icmpge搀庶、if_acmpeq拐纱、if_acmpne
- 復(fù)合條件分支:tableswitch疯淫、lookupswitch
- 無(wú)條件分支:goto、goto_w戳玫、jsr熙掺、jsr_w、ret
方法調(diào)用和返回指令
- invokevirtual指令:用于調(diào)用對(duì)象的實(shí)例方法咕宿,根據(jù)對(duì)象的實(shí)際類(lèi)型進(jìn)行分派
- invokeinterface指令:用于調(diào)用接口方法
- invokespecial指令:用于調(diào)用一些需要特殊處理的實(shí)例方法币绩,包括實(shí)例初始化方法、私有方法和父類(lèi)方法
- invokestatic指令:用于調(diào)用類(lèi)方法(static方法)
- invokedynamic指令:用于在運(yùn)行時(shí)動(dòng)態(tài)解析出調(diào)用點(diǎn)限定符所引用的方法府阀,并執(zhí)行該方法
異常處理指令
在java程序中缆镣,顯式拋出異常的操作都由athrow指令來(lái)實(shí)現(xiàn)。而在java虛擬機(jī)中试浙,處理異常不是由字節(jié)碼指令來(lái)實(shí)現(xiàn)的董瞻,而是采用異常表來(lái)完成的
同步指令
java虛擬機(jī)可以支持方法級(jí)的同步和方法內(nèi)部一段指令序列的同步,這兩種同步結(jié)構(gòu)都是使用管程(Monitor)來(lái)支持的田巴。方法級(jí)的同步是隱式的钠糊,利用方法表結(jié)構(gòu)中的ACC_SYNCHRONIZED訪問(wèn)標(biāo)志得知。指令序列的同步是由monitorenter和monitorexit兩條指令支持壹哺。