? ? Write Once Run Anywhere。各種不同平臺的虛擬機與所有平臺都統(tǒng)一使用的程序存儲格式---字節(jié)碼(ByteCode)是構(gòu)成平臺無關(guān)性的基石传惠。
? ? 語言無關(guān)性也越來越被開發(fā)者重視,實現(xiàn)語言無關(guān)性的基礎(chǔ)仍然是虛擬機和字節(jié)碼存儲格式南用。
一耗跛、Class類文件的結(jié)構(gòu)
? ? ? Class文件是一組以8位字節(jié)為基礎(chǔ)單位的二進制流口柳,各個數(shù)據(jù)項目嚴(yán)格按照順序緊湊地排列在Class文件之中缠俺,中間沒有添加任何分隔符显晶,這使得整個Class文件中存儲的內(nèi)容幾乎全部是程序運行的必要數(shù)據(jù),沒有空隙存在壹士。當(dāng)遇到需要占用8位字節(jié)以上的空間的數(shù)據(jù)項時磷雇,則會按照高位在前的方式分割成若干個0位字節(jié)進行存儲。Class文件中只有兩種數(shù)據(jù)類型:無符號數(shù)和表躏救。
? ? ? 無符號數(shù)屬于基本數(shù)據(jù)類型倦春,以u1,u2落剪,u4,u8來分別代表1個字節(jié)尿庐、2個字節(jié)忠怖、4個字節(jié)、8個字節(jié)的無符號數(shù)抄瑟,無符號數(shù)可以用來描述數(shù)字凡泣、索引引用、數(shù)量值或者按照UTF-8編碼構(gòu)成字符串皮假。
? ? ? 表是由多個無符號數(shù)或者其他表作為數(shù)據(jù)項構(gòu)成的符合數(shù)據(jù)類型鞋拟,所有表都習(xí)慣性地以“_info”結(jié)尾。表用于描述有層次關(guān)系的符合結(jié)構(gòu)的數(shù)據(jù)惹资,整個Class文件本質(zhì)上就是一張表贺纲。
? ? ? 表6-1
1、魔數(shù)與Class文件的版本
? ? ? 每個Class文件的頭4個字節(jié)稱為魔數(shù)(Magic Number)褪测,它的唯一作用是確定這個文件是否為一個虛擬機接受的Class文件猴誊。緊接著魔數(shù)的4個字節(jié)存儲的是Class文件的版本號:第5和第6個字節(jié)是次版本號(Minor Version),第7和第8個字節(jié)是主版本號(Major Version)侮措。Java 的版本號是從45開始的懈叹,高版本的JDK能向下兼容以前版本的Class文件,不能執(zhí)行超過其版本號的Class文件分扎。表中列出了從JDK1.1到JDK1.7澄成,主流JDK版本編譯器輸出的默認和可支持的Class文件版本號。
? ? 表6-2 class文件版本號
2、常量池
? ? ? 緊接著主次版本號之后的是常量池入口墨状,常量池可以理解為Class文件中的資源倉庫卫漫,它是Class文件結(jié)構(gòu)中與其他項目關(guān)聯(lián)最多的數(shù)據(jù)類型,也是占用Class文件空間最大的數(shù)據(jù)項目之一歉胶,同時它還是在Class文件中第一個出現(xiàn)的表類型數(shù)據(jù)項目汛兜。
? ? ? 由于常量池中常量的數(shù)量不是固定的,所以在常量池的入口需要放置一項u2類型的數(shù)據(jù)通今,代表常量池容量計數(shù)值(constant_pool_count)粥谬。這個容量計數(shù)是從1開始的。
? ? ? ? 常量池中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)辫塌。字面量比較接近于Java語言層面的常量概念漏策,如文本字符串,聲明為final的常量值等臼氨。而符號引用則屬于編譯原理方面的概念掺喻,包括了下面三類常量:類和接口的全限定名(Full Qualified Name)、字段的名稱和描述符(Descriptor)储矩、方法的名稱和描述符感耙。
? ? ? 常量池中每一項常量都是一個表,總共有14中標(biāo)持隧,都有一個共同的特點即硼,就是開始的第一位是一個u1類型的標(biāo)志位(tag),代表當(dāng)前這個常量屬于哪種常量類型屡拨。
? ? ? 表6-3 常量池的項目類型
? ? ? ? 之所以說常量池是最繁瑣的的數(shù)據(jù)只酥,是因為這14種常量類型各自均有自己的結(jié)構(gòu)。CONSTANT_Class_info類型的常量代表一個類或者接口的符號引用呀狼。表6-4
? ? ? ? tag是標(biāo)志位裂允,它用于區(qū)分常量類型;name_index是一個索引值哥艇,它指向常量池中一個CONSTANT_Uft8_info類型常量绝编,此常量代表了這個類(或者接口)的全限定名。表6-5
? ? ? ? ? length值說明了這個UTF-8編碼的字符串長度是多少字節(jié)貌踏,它后面緊跟著的長度為length字節(jié)的連續(xù)數(shù)據(jù)是一個使用UTF-8縮略編碼表示的字符串瓮增。由于Class文件中方法、字段等都需要引用CONSTANT_Uft8_info型常量來描述名稱哩俭,所以CONSTANT_Uft8_info型常量的最大長度也就是Java中方法绷跑、字段名的最大長度。而length最大值為65535.所以Java程序中不能定義超過64KB英文字符的變量或方法名凡资,否則將會無法編譯砸捏。表6-6
3谬运、訪問標(biāo)志
? ? ? 在常量池結(jié)束之后,緊接著的兩個字節(jié)代表訪問標(biāo)志(access_flags)垦藏,這個標(biāo)志用于識別一些類或者接口層次的訪問信息梆暖,包括:這個Class是類還是接口;是否定義為public類型掂骏;是否定義為abstract類型轰驳;如果是類的話,是否被聲明為final等弟灼。具體見下表级解。
? ? ? 表6-7 訪問標(biāo)志
? ? ? access_flag中一共有16個標(biāo)志位可以使用,當(dāng)前只定義了其中8個田绑,沒有使用到的標(biāo)志要求一律為0勤哗。
4、類索引掩驱、父類索引與接口索引集合
? ? 類索引(this_class)和父類索引(super_class)都是一個u2類型的數(shù)據(jù)芒划,而接口索引集合(interface)是一組u2類型的數(shù)據(jù)的集合,Class文件中由這三項數(shù)據(jù)來確定這個類的繼承關(guān)系欧穴。類索引用于確定這個類的全限定名民逼,父類索引用于確定這個類的父類的全限定名。
? ? 類索引涮帘、父類索引和接口索引集合都按順序排列在訪問標(biāo)志之后后拼苍,類索引和父類索引用兩個u2類型的索引值表示,他們各自指向一個類型為CONSTANT_Class_info的描述符常量焚辅,通過CONSTANT_Class_info類型的常量中的索引值可以找到定義在CONSTANT_Utf8_info類型的常量中的全限定名字符串。
? ? ? 對于接口索引集合苟鸯,入口的第一項----u2類型的數(shù)據(jù)為接口計數(shù)器(interfaces_count)同蜻,表示索引表的容量。如果該類沒有實現(xiàn)任何接口早处,則該計數(shù)器值為0湾蔓,后面接口的索引表不再占用任何字節(jié)。
5砌梆、字段表集合
? ? ? 字段表(field_info)用于描述接口或者類中聲明的變量默责。字段(field)包括類級變量以及實例級變量,但不包括在方法內(nèi)部聲明的局部變量咸包。
? ? ? 表6-8 字段表結(jié)構(gòu)
? ? ? 字段修飾符放在access_flags項目中桃序,它與類中的access_flags項目是非常類似的,都是一個u2的數(shù)據(jù)類型烂瘫。
? ? ? ? 表6-9 字段訪問標(biāo)志
? ? ? ? 在實際情況中媒熊,ACC_PUBLIC、ACC_PRIVATE 、ACC_PROTECTED三個標(biāo)志最多只能選擇其中一個芦鳍,ACC_FINAL 嚷往、ACC_VOLATILE不能同時選擇。接口之中的字段必須有ACC_PUBLIC 柠衅、ACC_STATIC皮仁、 ACC_STATIC、 ACC_FINAL標(biāo)志菲宴,這些是由Java語言規(guī)則決定的贷祈。
? ? ? ? 緊跟access_flags標(biāo)志的是兩項索引值:name_index和descriptor_index。它們都是對常量池的引用裙顽,分別代表著字段的簡單名稱以及字段和方法的描述符付燥。
? ? ? ? 字段和方法的描述符的作用是用來描述字段的數(shù)據(jù)類型、方法的參數(shù)列表(包括數(shù)量愈犹、類型以及順序)和返回值键科。根據(jù)描述符規(guī)則,基本數(shù)據(jù)類型(byte漩怎、char勋颖、double 、float勋锤、 int饭玲、 long、 short叁执、 boolean)以及代表無返回值的void類型都用一個大寫字母來表示茄厘,而對象類型則用字符L加對象的全限定名來表示。表6-10谈宛。
6次哈、方法表集合
? ? ? 方法表結(jié)構(gòu)如同字段表一樣,依次包括了訪問標(biāo)志(access_flag)吆录、名稱索引(name_index)窑滞、描述符索引(descriptor_index)、屬性表集合(attribute)幾項恢筝,這些數(shù)據(jù)項目的含義也類似哀卫,僅在訪問標(biāo)志和屬性表集合的可選項中有所區(qū)別。
? ? ? 表6-11 方法表結(jié)構(gòu)
? ? ? ? 因為volatile關(guān)鍵字和transient關(guān)鍵字不能修飾方法撬槽,所以方法表的訪問標(biāo)志中沒有了ACC_VOLATILE標(biāo)志和ACC_TRANSIENT標(biāo)志此改。與之相對的,synchronized侄柔、native带斑、strictfp和abstract關(guān)鍵字可以修飾方法鼓寺,所以方法表的訪問標(biāo)志中增加了ACC_SYNCHRONIXED 、ACC_NATIVE勋磕、 ACC_STRICTFP和ACC_ABSTRACT標(biāo)志妈候。對于方法表,所有標(biāo)志位及其取值見表6-12挂滓。
7苦银、屬性表集合
? ? ? 屬性表(attribute_info),在Class文件赶站、字段表吸祟、方法表都可以攜帶自己的屬性表集合焰枢,用于描述某些場景專有的信息。表6-13
對于每個屬性,它的名稱需要從常量池中引用一個CONSTANT_Utf8_info類型的常量來表示碰镜,而屬性值的結(jié)構(gòu)則是完全自由定義的褪子,只需要通過一個u4的長度屬性去說明屬性值所占用的位數(shù)即可百揭。
? 表6-14 屬性表結(jié)構(gòu)
? Code屬性
? ? Java程序方法體中的代碼經(jīng)過Javac編譯器處理后塞俱,最終變?yōu)樽止?jié)碼指令存儲在COde屬性內(nèi)。Code屬性出現(xiàn)在方法表的屬性集合中渣窜,但并非所有方法表都必須存在這個屬性铺根,譬如接口或者抽象類中的方法就不存在Code屬性,如果方法表有Code屬性存在乔宿,那么它的結(jié)構(gòu)如表6-15.
? ? 表6-15 Code屬性表結(jié)構(gòu)
? ? attribute_name_index是一項指向CONSTANT_Utf8_info型常量的索引位迂,常量值固定位“Code”,它代表了該屬性的屬性名稱详瑞,attribute_length指示了屬性值的長度掂林,由于屬性名稱索引與屬性長度一共為6個字節(jié),所以屬性值的長度固定位整個屬性表長度減去6個字節(jié)坝橡。
? ? max_stack代表了操作數(shù)棧(Operand Stack)深度的最大值泻帮。在方法執(zhí)行的任意時刻,操作數(shù)棧都不會超過這個深度驳庭。虛擬機運行的時候需要根據(jù)這個值來分配棧幀(Stack Frame)中的操作棧深度刑顺。
? ? max_locals代表了局部變量表所需要的存儲空間氯窍。在這里饲常,max_locals的單位是Slot,Slot是虛擬機為局部變量分配內(nèi)存所使用的最小單位狼讨。
? ? code_length和code用來存儲Java源程序編譯后生成的字節(jié)碼指令贝淤。code_length代表字節(jié)碼長度,code是用于存儲字節(jié)碼指令的一系列字節(jié)流政供。
? ? Code屬性是class文件中最重要的一個屬性播聪。
? ? Exceptions屬性朽基,作用是列舉出方法中可能拋出的受檢查異常(Checked Exception),也就是方法描述時在throws關(guān)鍵字后面列舉異常离陶。LineNumberTable屬性稼虎,用于描述Java源碼行號與字節(jié)碼行號之間的對應(yīng)關(guān)系。LocalVariableTable屬性招刨,用于描述棧幀中局部變量表中的變量與Java源碼中定義的變量之間的關(guān)系霎俩。SourceFile屬性,用于記錄生成這個Class文件的源碼文件名稱沉眶。ConstantValue屬性的作用是通知虛擬機自動為靜態(tài)變量賦值打却。只有被static關(guān)鍵字修飾的變量才可以使用這項屬性。InnerClasses屬性用于記錄內(nèi)部類與宿主類之間的關(guān)聯(lián)谎倔。Deprecated以及Synthetic兩個屬性都屬于標(biāo)志類型的布爾屬性柳击,只存在有和沒有的區(qū)別,沒有屬性值的概念片习。StackMapTable屬性捌肴,它是一個復(fù)雜的變長屬性,位于Code屬性的屬性表中毯侦。Signature屬性哭靖,它是一個可選的定長屬性,可以出現(xiàn)在類侈离、字段表和方法表結(jié)構(gòu)的屬性表中试幽。BootstrapMethods屬性,它是一個復(fù)雜的變長屬性卦碾,位于類文件的屬性表中铺坞。
? ? ? 以上內(nèi)容就是java字節(jié)碼文件的結(jié)構(gòu),java字節(jié)碼還有字節(jié)碼指令洲胖,這里就不多贅述了济榨。有興趣可看《深入Java虛擬機-JVM高級特性與最佳實踐》---周志華這本書,挺不錯的绿映。
本文來自于《深入Java虛擬機-JVM高級特性與最佳實踐》---周志明擒滑。如果侵權(quán),請聯(lián)系作者刪除叉弦。