Technical--Documentation
共享技術(shù)文檔
項(xiàng)目簡(jiǎn)介
為為日常工作和學(xué)習(xí)的總結(jié)。
技術(shù)文檔主站簡(jiǎn)介
主站文檔是我在前人的腳步上對(duì)研發(fā)的學(xué)習(xí)和總結(jié)紀(jì)錄,在以后的日子里,我會(huì)將自己研發(fā)工作中所可能遇見問題和心得在這里紀(jì)錄下來,公開的分享。世界的存在很美好,開源的存在很精彩。從今天起,我便借助開源的力量,向改變?nèi)祟惖纳罘绞竭@條路邁進(jìn)。
點(diǎn)擊這里直接進(jìn)入為為技術(shù)文檔主站,或者訪問 https://weiwei02.github.io/Technical--Documentation/
與文檔配套的 Technical--Documentation項(xiàng)目的地址是 https://github.com/weiwei02/Technical--Documentation
*.class文件介紹
一般來講.class文件是.java文件在編譯器編譯后生成的jvm能夠運(yùn)行的文件,*.class文件又常被稱為字節(jié)碼文件。java在創(chuàng)始之初,就提倡“一次編寫,處處運(yùn)行的概念”,在當(dāng)今編程圈中這個(gè)概念早已不是什么特例。java通過將開發(fā)人員所編寫的java代碼編譯成class文件,然后由jvm虛擬機(jī)在執(zhí)行時(shí)將不分平臺(tái)的class文件中的字節(jié)碼,再翻譯成機(jī)器碼,交給硬件執(zhí)行。java就是靠jvm虛擬機(jī)的這個(gè)設(shè)計(jì)來實(shí)現(xiàn)與平臺(tái)無關(guān)的特性的。class文件不但與硬件平臺(tái)和操作系統(tǒng)無關(guān),也和具體的編程語言無關(guān),就目前來說,如函數(shù)式編程語言scala與Groovy都可以通過自己的編譯器將源代碼編譯成class文件,在jvm上運(yùn)行。
綜合來講,class文件有以下兩點(diǎn)特性:
- 與硬件和操作系統(tǒng)平臺(tái)無關(guān)
- 與源碼所使用的編程語言無關(guān)
class類文件的結(jié)構(gòu)
每一個(gè)class文件都唯一對(duì)應(yīng)著java類或接口枚舉等定義信息,但類不一定都定義在class文件中,類可能是由類加載器動(dòng)態(tài)生成的。
class文件所以被稱之為字節(jié)碼據(jù)我猜測(cè)可能是因?yàn)閏lass文件以8位(1字節(jié))為單位進(jìn)行存儲(chǔ)的二進(jìn)制信息。字節(jié)碼中各個(gè)數(shù)據(jù)項(xiàng)目嚴(yán)格按照順序緊湊的排列,中間沒有任何分割符。在需要存儲(chǔ)整型或浮點(diǎn)型這些大于8位的數(shù)據(jù)項(xiàng)目時(shí),則會(huì)使用Big-Endian的字節(jié)序進(jìn)行存儲(chǔ),將最高位字節(jié)放在地址最低位,最低位字節(jié)放在地址最高位。
class文件格式采用表來存儲(chǔ)數(shù)據(jù),表中有無符號(hào)數(shù)和表兩種數(shù)據(jù)類型。對(duì)于這兩種數(shù)據(jù)類型說明如下:
- 無符號(hào)數(shù): 無符號(hào)數(shù)是基本的數(shù)據(jù)類型,可以用來表示數(shù)字、索引引用、數(shù)量值或者按照UTF-8編碼構(gòu)成的字符串。如u1,u2,u4,u8分別代表1,2,4,8個(gè)字節(jié)字節(jié)的無符號(hào)數(shù)。
- 表: 表是由多個(gè)無符號(hào)數(shù)或者其它表作為數(shù)據(jù)項(xiàng)所組成的復(fù)合數(shù)據(jù)結(jié)構(gòu),表用于描述層次關(guān)系和復(fù)合的數(shù)據(jù)結(jié)構(gòu),整個(gè)class文件就是一張表。通常表以 _info 結(jié)尾。如表1就是一個(gè)class的表示例。
| 類型 | 名稱 | 數(shù)量 |
|---|---|---|
| u4 | magic | 1 |
| u2 | minor_version | 1 |
| u2 | major_version | 1 |
| u2 | constant_pool_count | 1 |
| cp_info | constant_pool | constant_pool_count - 1 |
| u2 | asscess_flags | 1 |
| u2 | this_class | 1 |
| u2 | super_class | 1 |
| u2 | interfaces_count | 1 |
| u2 | interfaces | interfaces_count |
| u2 | fields_count | 1 |
| field_info | fields | fields_count |
| u2 | methods_count | 1 |
| method_info | methods | method_count |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_count |
表 1 class文件格式
class文件的順序和格式必須嚴(yán)格實(shí)現(xiàn)按照上述規(guī)則,否則jvm將不能識(shí)別執(zhí)行。
Magic Number 魔數(shù)
魔數(shù)是一個(gè)和文件后綴名相似的用于文件格式識(shí)別的約定,一般規(guī)定文件內(nèi)容開頭的前幾個(gè)字節(jié)為文件的魔數(shù)。不同于文件后綴名很容易被用戶以重命名的方式進(jìn)行更改,文件的魔數(shù)作為識(shí)別手段可以更安全的確定文件的可用性。
class文件使用前4個(gè)字節(jié)作為魔數(shù),來確定.class文件是否是一個(gè)能夠被虛擬機(jī)識(shí)別的文件,其值是 0xCAFFEBABE 。
版本
class文件的第5-6個(gè)字節(jié)代表的可執(zhí)行該class文件的目標(biāo)虛擬機(jī)的最低次版本號(hào)(Minor Version),第7-8個(gè)字節(jié)是主版本號(hào)(Major Version)。java虛擬機(jī)可以運(yùn)行比當(dāng)前虛擬機(jī)版本號(hào)低的class文件,拒絕運(yùn)行版本號(hào)不合法,或比自己版本高的class文件。JDK1.1的版本號(hào)是45,之后的每個(gè)大版本發(fā)布都把主版本號(hào)加1,如JDK1.2主版本號(hào)是46,JDK8的版本號(hào)是52。
JDK在編譯java文件是可以通過 javac -target 1.6 ...命令來指定編譯后的class文件可以在1.6的虛擬機(jī)版本上運(yùn)行。
常量池
常量池是class文件中第一個(gè)表類型的數(shù)據(jù)項(xiàng)目,常量池是class文件中的資源倉(cāng)庫(kù),是class文件結(jié)構(gòu)中與其它項(xiàng)目關(guān)聯(lián)最多的數(shù)據(jù)類型,也是占用class文件空間最大的數(shù)據(jù)項(xiàng)目之一。
class文件里緊隨版本之后的數(shù)據(jù)項(xiàng)是常量池,由于常量池的數(shù)量是不固定的,所以在常量池的數(shù)據(jù)項(xiàng)之前放置的有一個(gè)u2類型的數(shù)據(jù),代表常量池的大小。常量池大小的初始值是1,如常量池的大小的數(shù)據(jù)如果顯示的是10,就代表該class文件中有9個(gè)常量。
常量池中主要存放兩大類常量:Literal(字面常量)、Symbolic Reference(符號(hào)引用常量)。字面常量類似于java中常量的概念,如文本字符串、final關(guān)鍵字所聲明的常量等。而符號(hào)引用常量則是編譯中的概念,主要包括以下三種類型的常量。
- 符號(hào)常量
+ 類和接口的全限定名(Fully Qualified Name)
+ 字段名稱和描述符(Descriptor)
+ 方法的名稱和描述符
class文件中不會(huì)保存各個(gè)方法或字段在內(nèi)存中的布局信息,而是在虛擬機(jī)加載class文件時(shí)進(jìn)行動(dòng)態(tài)的連接。虛擬機(jī)在運(yùn)行class文件時(shí)從常量池中獲取對(duì)應(yīng)的符號(hào)引用,再在創(chuàng)建類或者運(yùn)行時(shí)解析連接到具體的內(nèi)存地址當(dāng)中。
常量池中的每一個(gè)常量都是一個(gè)表,在JDK8中有14種表結(jié)構(gòu)的常量表。如表 2 所示。
| 類型 | 標(biāo)識(shí) | 描述 |
|---|---|---|
| CONSTANT_utf8_info | 1 | UTF-8編碼的字符串 |
| CONSTANT_Integer_info | 3 | 整形字面量 |
| CONSTANT_Float_info | 4 | 浮點(diǎn)型字面量 |
| CONSTANT_Long_info | 5 | 長(zhǎng)整型字面量 |
| CONSTANT_Double_info | 6 | 雙精度浮點(diǎn)型字面量 |
| CONSTANT_Class_info | 7 | 類或接口的符號(hào)引用 |
| CONSTANT_String_info | 8 | 字符串類型字面量 |
| CONSTANT_Fieldref_info | 9 | 字段的符號(hào)引用 |
| CONSTANT_Methodref_info | 10 | 類中方法的符號(hào)引用 |
| CONSTANT_InterfaceMethodref_info | 11 | 接口中方法的符號(hào)引用 |
| CONSTANT_NameAndType_info | 12 | 字段或方法的符號(hào)引用 |
| CONSTANT_MothodType_info | 16 | 標(biāo)志方法類型 |
| CONSTANT_MethodHandle_info | 15 | 表示方法句柄 |
| CONSTANT_InvokeDynamic_info | 18 | 表示一個(gè)動(dòng)態(tài)方法調(diào)用點(diǎn) |
表 2 常量池?cái)?shù)據(jù)項(xiàng)目類型表
這14種常量結(jié)構(gòu)表開始的第一個(gè)字節(jié)都是一個(gè)u1類型的標(biāo)識(shí)位,其值就是表2中每項(xiàng)常量表類型所對(duì)應(yīng)的標(biāo)識(shí)列的值,代表當(dāng)前常量屬于哪種常量類型。這14種常量類型各自有自己不同的表結(jié)構(gòu),詳情如表 3 所示。
| 常量 | 項(xiàng)目 | 類型 | 描述 |
|---|---|---|---|
| CONSTANT_Utf8_info | tag | u1 | 值為1 |
| length | u2 | UTF-8編碼的字符串占用的字節(jié)數(shù) | |
| bytes | u1 | utf-8編碼的字符串 | |
| CONSTANT_Integer_info | tag | u1 | 值為3 |
| bytes | u4 | 按照Big-Endian存儲(chǔ)的int值 | |
| CONSTANT_Float_info | tag | u1 | 4 |
| bytes | u4 | 按照Big-Endian存儲(chǔ)的float值 | |
| CONSTANT_Long_info | tag | u1 | 5 |
| bytes | u8 | 按照Big-Endian存儲(chǔ)的long值 | |
| CONSTANT_Double_info | tag | u1 | 6 |
| bytes | u8 | 按照Big-Endian存儲(chǔ)的long值double值 | |
| CONSTANT_Class_info | tag | u1 | 7 |
| index | u2 | 指向全限定名常量項(xiàng)的索引 | |
| CONSTANT_String_info | tag | u1 | 8 |
| index | u2 | 指向字符串常量的索引 | |
| CONSTANT_Fieldref_info | tag | u1 | 9 |
| index | u2 | 指向聲明字段的類或接口描述符CONSTANT_Class_info的索引值 | |
| index | u2 | 指向CONSTANT_NameAndType_info的索引值 | |
| CONSTANT_Methodref_info | tag | u1 | 10 |
| index | u2 | 指向聲明方法的類描述符CONSTANT_Class_info的索引值 | |
| index | u2 | 指向CONSTANT_NameAndType_info的索引值 | |
| CONSTANT_InterfaceMethodref_info | tag | u1 | 11 |
| index | u2 | 指向聲明方法的接口描述符CONSTANT_Class_info的索引值 | |
| index | u2 | 指向CONSTANT_NameAndType_info的索引值 | |
| CONSTANT_NameAndType_info | tag | u1 | 12 |
| index | u2 | 指向該字段或方法名稱常量的索引值 | |
| index | u2 | 指向該字段或方法描述符常量的索引值 | |
| CONSTANT_MethodHandle_info | tag | u1 | 15 |
| reference_kind | u1 | 值必須1~9,它決定了方法句柄的的類型。方法句柄類型的值表示方法句柄的字節(jié)碼行為 | |
| reference_index | u2 | 對(duì)常量池的有效索引 | |
| CONSTANT_MethodType_info | tag | u1 | 16 |
| description_index | u2 | 對(duì)常量池中方法描述符的有效索引常量池在該處的索引必須是CONSTANT_Utf8_info的結(jié)構(gòu),表示方法的描述符。 | |
| CONSTANT_InvokeDynamic_info | tag | u1 | 18 |
| bootstap_method_attr_index | u2 | 對(duì)當(dāng)前class文件中引導(dǎo)方法表的bootstap_methods[]數(shù)組的有效索引 | |
| name_and_type_index | u2 | 對(duì)當(dāng)前常量池的有效索引,常量池在此處必須是CONSTANT_NameAndType_info結(jié)構(gòu),表示方法名和方法描述。 |
表 3 常量池中14中常量的結(jié)構(gòu)總表
在表 3 中可以看到class文件所支持的所有的常量的結(jié)構(gòu)信息,另外由于class中的類名、方法、字段都要用CONSTANT_Utf8_info型的常量來描述名稱,所以CONSTANT_Utf8_info的最大長(zhǎng)度也是java中類名、方法名或字段的最大長(zhǎng)度65535,如果超出這個(gè)最大長(zhǎng)度,便會(huì)無法編譯。
訪問權(quán)限標(biāo)識(shí)
引用
在寫作本文的過程中引用了以下資料,為為在此深深謝過以下資料的作者。
- 《The Java Virtual Machine Specification》
- 《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐/周志明著.——2版.——北京:機(jī)械工業(yè)出版社,2013.6》
關(guān)于
本項(xiàng)目和文檔中所用的內(nèi)容僅供學(xué)習(xí)和研究之用,轉(zhuǎn)載或引用時(shí)請(qǐng)指明出處。如果你對(duì)文檔有疑問或問題,請(qǐng)?jiān)陧?xiàng)目中給我留言或發(fā)email到 weiwei02@vip.qq.com
from weiwei.wang 20170625