摘要:在屬性中,和分別用于存儲(chǔ)字節(jié)碼長(zhǎng)度和字節(jié)碼指令,每條指令即一個(gè)字節(jié)類型。在虛擬機(jī)執(zhí)行時(shí),通過(guò)讀取中的一個(gè)個(gè)字節(jié)碼,并將字節(jié)碼翻譯成相應(yīng)的指令。另外,雖然是一個(gè)類型的值,但是實(shí)際上一個(gè)方法不允許超過(guò)條字節(jié)碼指令。
最近在寫一個(gè)私人項(xiàng)目,名字叫做ClassAnalyzer,ClassAnalyzer的目的是能讓我們對(duì)Java Class文件的設(shè)計(jì)與結(jié)構(gòu)能夠有一個(gè)深入的理解。主體框架與基本功能已經(jīng)完成,還有一些細(xì)節(jié)功能日后再增加。實(shí)際上JDK已經(jīng)提供了命令行工具javap來(lái)反編譯Class文件,但本篇文章將闡明我實(shí)現(xiàn)解析器的思路。
Class文件作為類或者接口信息的載體,每個(gè)Class文件都完整的定義了一個(gè)類。為了使Java程序可以“編寫一次,處處運(yùn)行”,Java虛擬機(jī)規(guī)范對(duì)Class文件進(jìn)行了嚴(yán)格的規(guī)定。構(gòu)成Class文件的基本數(shù)據(jù)單位是字節(jié),這些字節(jié)之間不存在任何分隔符,這使得整個(gè)Class文件中存儲(chǔ)的內(nèi)容幾乎全部是程序運(yùn)行的必要數(shù)據(jù),單個(gè)字節(jié)無(wú)法表示的數(shù)據(jù)由多個(gè)連續(xù)的字節(jié)來(lái)表示。
根據(jù)Java虛擬機(jī)規(guī)范,Class文件采用一種類似于C語(yǔ)言結(jié)構(gòu)體的偽結(jié)構(gòu)來(lái)存儲(chǔ)數(shù)據(jù),這種偽結(jié)構(gòu)中只有兩種數(shù)據(jù)類型:無(wú)符號(hào)數(shù)和表。Java虛擬機(jī)規(guī)范定義了u1、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ù)量值或者是字符串。表是由多個(gè)無(wú)符號(hào)數(shù)或者其它表作為數(shù)據(jù)項(xiàng)構(gòu)成的符合數(shù)據(jù)類型,表用于描述有層次關(guān)系的符合結(jié)構(gòu)的數(shù)據(jù),因此整個(gè)Class文件本質(zhì)上就是一張表。在ClassAnalyzer中u1、u2、u4和u8分別對(duì)應(yīng)于byte、short、int和long,Class文件被描述為如下Java類。
public class ClassFile { public U4 magic; // magic public U2 minorVersion; // minor_version public U2 majorVersion; // major_version public U2 constantPoolCount; // constant_pool_count public ConstantPoolInfo[] cpInfo; // cp_info public U2 accessFlags; // access_flags public U2 thisClass; // this_class public U2 superClass; // super_class public U2 interfacesCount; // interfaces_count public U2[] interfaces; // interfaces public U2 fieldsCount; // fields_count public FieldInfo[] fields; // fields public U2 methodsCount; // methods_count public MethodInfo[] methods; // methods public U2 attributesCount; // attributes_count public BasicAttributeInfo[] attributes; // attributes }如何解析
組成Class文件的各個(gè)數(shù)據(jù)項(xiàng)中,例如魔數(shù)、Class文件的版本等數(shù)據(jù)項(xiàng)、訪問標(biāo)志、類索引、父類索引,它們?cè)诿總€(gè)Class文件中都占用固定數(shù)量的字節(jié),在解析時(shí)只需要讀取相應(yīng)數(shù)量的字節(jié)。除此之外,需要靈活處理的主要包括4部分:常量池、字段表集合、方法表集合和屬性表集合。字段和方法都可以具備自己的屬性,Class本身也有相應(yīng)的屬性,因此,在解析字段表集合和方法表集合的同時(shí)也包含了屬性表的解析。
常量池占據(jù)了Class文件很大一部分的數(shù)據(jù),用于存儲(chǔ)所有的常量信息,包括數(shù)字和字符串常量、類名、接口名、字段名和方法名等。Java虛擬機(jī)規(guī)范定義了多種常量類型,每一種常量類型都有自己的結(jié)構(gòu)。常量池本身是一個(gè)表,在解析時(shí)有幾點(diǎn)需要注意。
每個(gè)常量類型都通過(guò)一個(gè)u1類型的tag來(lái)標(biāo)識(shí)。
表頭給出的常量池大小(constantPoolCount)比實(shí)際大1,例如,如果constantPoolCount等于47,那么常量池中有46項(xiàng)常量。
常量池的索引范圍從1開始,例如,如果constantPoolCount等于47,那么常量池的索引范圍為1~46。設(shè)計(jì)者將第0項(xiàng)空出來(lái)的目的是用于表達(dá)“不引用任何一個(gè)常量池項(xiàng)目”。
CONSTANT_Utf8_info型常量的結(jié)構(gòu)中包含u1類型的tag、u2類型的length和由length個(gè)u1類型組成的bytes,這length字節(jié)的連續(xù)數(shù)據(jù)是一個(gè)使用MUTF-8(Modified UTF-8)編碼的字符串。MUTF-8與UTF-8并不兼容,主要區(qū)別有兩點(diǎn):一是null字符會(huì)被編碼成2字節(jié)(0xC0和0x80);二是補(bǔ)充字符是按照UTF-16拆分為代理對(duì)分別編碼的,相關(guān)細(xì)節(jié)可以看這里(變種UTF-8)。
屬性表用于描述某些場(chǎng)景專有的信息,Class文件、字段表和方法表都有相應(yīng)的屬性表集合。Java虛擬機(jī)規(guī)范定義了多種屬性,ClassAnalyzer目前實(shí)現(xiàn)了對(duì)常用屬性的解析。和常量類型的數(shù)據(jù)項(xiàng)不同,屬性并沒有一個(gè)tag來(lái)標(biāo)識(shí)屬性的類型,但是每個(gè)屬性都包含有一個(gè)u2類型的attribute_name_index,attribute_name_index指向常量池中的一個(gè)CONSTANT_Utf8_info類型的常量,該常量包含著屬性的名稱。在解析屬性時(shí),ClassAnalyzer正是通過(guò)attribute_name_index指向的常量對(duì)應(yīng)的屬性名稱來(lái)得知屬性的類型。
字段表用于描述類或者接口中聲明的變量,字段包括類級(jí)變量以及實(shí)例級(jí)變量。字段表的結(jié)構(gòu)包含一個(gè)u2類型的access_flags、一個(gè)u2類型的name_index、一個(gè)u2類型的descriptor_index、一個(gè)u2類型的attributes_count和attributes_count個(gè)attribute_info類型的attributes。我們已經(jīng)介紹了屬性表的解析,attributes的解析方式與屬性表的解析方式一致。
Class的文件方法表采用了和字段表相同的存儲(chǔ)格式,只是access_flags對(duì)應(yīng)的含義有所不同。方法表包含著一個(gè)重要的屬性:Code屬性。Code屬性存儲(chǔ)了Java代碼編譯成的字節(jié)碼指令,在ClassAnalyzer中,Code對(duì)應(yīng)的Java類如下所示(僅列出了類屬性)。
public class Code extends BasicAttributeInfo { private short maxStack; private short maxLocals; private long codeLength; private byte[] code; private short exceptionTableLength; private ExceptionInfo[] exceptionTable; private short attributesCount; private BasicAttributeInfo[] attributes; ... private class ExceptionInfo { public short startPc; public short endPc; public short handlerPc; public short catchType; ... } }
在Code屬性中,codeLength和code分別用于存儲(chǔ)字節(jié)碼長(zhǎng)度和字節(jié)碼指令,每條指令即一個(gè)字節(jié)(u1類型)。在虛擬機(jī)執(zhí)行時(shí),通過(guò)讀取code中的一個(gè)個(gè)字節(jié)碼,并將字節(jié)碼翻譯成相應(yīng)的指令。另外,雖然codeLength是一個(gè)u4類型的值,但是實(shí)際上一個(gè)方法不允許超過(guò)65535條字節(jié)碼指令。
代碼實(shí)現(xiàn)ClassAnalyzer的源碼已放在了GitHub上。在ClassAnalyzer的README中,我以一個(gè)類的Class文件為例,對(duì)該Class文件的每個(gè)字節(jié)進(jìn)行了分析,希望對(duì)大家的理解有所幫助。
參考深入理解Java虛擬機(jī)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/66642.html
摘要:最終形成可以被虛擬機(jī)最直接使用的類型的過(guò)程就是虛擬機(jī)的類加載機(jī)制。即重寫一個(gè)類加載器的方法驗(yàn)證驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。 《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐(第二版》讀書筆記與常見相關(guān)面試題總結(jié) 本節(jié)常見面試題(推薦帶著問題閱讀,問題答案在文中都有提到): 簡(jiǎn)單說(shuō)說(shuō)類加載過(guò)...
摘要:驗(yàn)證驗(yàn)證階段的主要目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。不同的虛擬機(jī)對(duì)類驗(yàn)證的實(shí)現(xiàn)可能會(huì)有所不同,但大致都會(huì)完成以下四個(gè)階段的驗(yàn)證文件格式的驗(yàn)證元數(shù)據(jù)的驗(yàn)證字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證。 原文地址 虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn),轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類型,Thisis ...
摘要:還設(shè)置一個(gè)攔截器來(lái)攔截國(guó)際化語(yǔ)言的變化。修改啟動(dòng)類攔截器現(xiàn)在我們?cè)龠\(yùn)行一下看看效果,看到每個(gè)鏈接都顯示的他們對(duì)應(yīng)的國(guó)際化信息里的內(nèi)容。 前言 公司將項(xiàng)目由Struts2轉(zhuǎn)到Springmvc了,由于公司業(yè)務(wù)是境外服務(wù),所以對(duì)國(guó)際化功能需求很高。Struts2自帶的國(guó)際化功能相對(duì)Springmvc來(lái)說(shuō)更加完善,不過(guò)spring很大的特性就是可定定制化性強(qiáng),所以在公司項(xiàng)目移植的到Sprin...
摘要:在屬性中,和分別用于存儲(chǔ)字節(jié)碼長(zhǎng)度和字節(jié)碼指令,每條指令即一個(gè)字節(jié)類型。在虛擬機(jī)執(zhí)行時(shí),通過(guò)讀取中的一個(gè)個(gè)字節(jié)碼,并將字節(jié)碼翻譯成相應(yīng)的指令。另外,雖然是一個(gè)類型的值,但是實(shí)際上一個(gè)方法不允許超過(guò)條字節(jié)碼指令。 最近在寫一個(gè)私人項(xiàng)目,名字叫做SmallVM,SmallVM的目的在于通過(guò)實(shí)現(xiàn)一個(gè)輕量級(jí)的Java虛擬機(jī),加深對(duì)Java虛擬機(jī)的認(rèn)知和理解。在Java虛擬機(jī)加載類的過(guò)程中,需要...
閱讀 2784·2021-09-01 10:30
閱讀 1680·2019-08-30 15:52
閱讀 965·2019-08-29 18:40
閱讀 1116·2019-08-28 18:30
閱讀 2391·2019-08-23 17:19
閱讀 1321·2019-08-23 16:25
閱讀 2700·2019-08-23 16:18
閱讀 2977·2019-08-23 13:53