之前還在美團(tuán)實(shí)習(xí)的時(shí)候捐韩,當(dāng)時(shí)讀《深入理解Java虛擬機(jī)》由于時(shí)間原因只總結(jié)了幾個(gè)章節(jié)殉挽,現(xiàn)在把余下的幾個(gè)章節(jié)補(bǔ)充上感耙,發(fā)表順序有些混亂就缆,章節(jié)主線詳見文章匯總|學(xué)習(xí)Android的一點(diǎn)一滴扰路。
本篇將介紹Class文件結(jié)構(gòu)中的各個(gè)組成部分尤溜,以及每個(gè)部分的定義、數(shù)據(jù)結(jié)構(gòu)和使用汗唱,有利于進(jìn)一步了解虛擬機(jī)執(zhí)行引擎宫莱。
- 概述
- 類文件結(jié)構(gòu)
- 字節(jié)碼指令
1.概述
運(yùn)行在各種不同平臺(tái)上的虛擬機(jī)通過載入和執(zhí)行同一種平臺(tái)無關(guān)的字節(jié)碼來實(shí)現(xiàn)了程序的“一次編寫,到處運(yùn)行”哩罪∈诎裕可見字節(jié)碼是構(gòu)成平臺(tái)無關(guān)性的基石。
Java虛擬機(jī)不和Java等任何語言綁定际插,只和存儲(chǔ)字節(jié)碼的Class文件這種特定的二進(jìn)制文件格式關(guān)聯(lián)碘耳,且并不關(guān)心Class的來源是何種語言,也體現(xiàn)了Java虛擬機(jī)的語言無關(guān)性框弛。
2.類文件結(jié)構(gòu)
- Class文件是一組以8位字節(jié)為基礎(chǔ)單位的二進(jìn)制流辛辨,各個(gè)數(shù)據(jù)項(xiàng)目嚴(yán)格按照順序緊湊地排列在Class文件之中,中間無任何分隔符,當(dāng)遇到需要占用8位字節(jié)以上空間的數(shù)據(jù)項(xiàng)時(shí)斗搞,會(huì)按照高位在前的方式分割成若干個(gè)8位字節(jié)進(jìn)行存儲(chǔ)指攒。
- Class文件格式采用一種類似于C語言結(jié)構(gòu)體的偽結(jié)構(gòu)來存儲(chǔ)數(shù)據(jù),包含兩種數(shù)據(jù)類型:
- 無符號(hào)數(shù):屬于基本數(shù)據(jù)類型僻焚;以u(píng)1允悦、u2、u4溅呢、u8來分別代表1個(gè)字節(jié)澡屡、2個(gè)字節(jié)、4個(gè)字節(jié)和8個(gè)字節(jié)的無符號(hào)數(shù)咐旧;可用于描述數(shù)字驶鹉、索引引用、數(shù)量值或按照UTF-8 編碼構(gòu)成的字符串值铣墨。
- 表:由多個(gè)無符號(hào)數(shù)或其他表作為數(shù)據(jù)項(xiàng)構(gòu)成的復(fù)合數(shù)據(jù)類型室埋;常以“_info”結(jié)尾;可用于描述有層次關(guān)系的復(fù)合結(jié)構(gòu)的數(shù)據(jù)伊约。
- 整個(gè)Class文件本質(zhì)上就是一張表姚淆,所包含的數(shù)據(jù)項(xiàng)如圖:
接下來依次介紹表中各個(gè)數(shù)據(jù)項(xiàng)的具體含義。
a. 魔數(shù)
- 魔數(shù)(Magic Number):每個(gè)Class文件的頭4個(gè)字節(jié)
- 作用:判斷該文件是否為一個(gè)能被虛擬機(jī)接受的Class文件
b.版本號(hào)
- 版本號(hào):包含主版本號(hào)和一系列次版本號(hào)
- 次版本號(hào)(Minor Version):第5和第6個(gè)字節(jié)
- 主版本號(hào)(Major Version):第7和第8個(gè)字節(jié)
- 作用:判斷該文件是否在虛擬機(jī)處理的有效范圍內(nèi)
c.常量池
- 常量池:使用一個(gè)前置的容量計(jì)數(shù)器(constant_pool_count)加上若干個(gè)連續(xù)的常量項(xiàng)(constant_pool)來描述
- 容量計(jì)數(shù)器:從1開始屡律,目的是滿足后面某些指向常量池的索引值的數(shù)據(jù)在特定情況下需要表達(dá)“不引用任何一個(gè)常量池項(xiàng)目”的含義腌逢,這時(shí)可以把索引值置為0來表示
- 常量項(xiàng):如constant_pool_count=2表示常量池中有1個(gè)常量項(xiàng)
- 特點(diǎn):是Class文件的資源倉庫、是Class文件結(jié)構(gòu)中與其他項(xiàng)目關(guān)聯(lián)最多的數(shù)據(jù)類型超埋、是占用Class文件空間最大的數(shù)據(jù)項(xiàng)目之一搏讶、是在Class文件中第一個(gè)出現(xiàn)的表類型數(shù)據(jù)項(xiàng)目
- 存放內(nèi)容:兩大類常量
- 字面量(Literal):指Java語言層面的常量概念,如文本字符串霍殴、聲明為final的常量值等
- 符號(hào)引用(Symbolic References):指編譯原理方面的概念媒惕,包含類和接口的全限定名(Fully Qualified Name)、字段的名稱和描述符(Descriptor)来庭、方法的名稱和描述符
Java代碼進(jìn)行Javac編譯的過程同虛擬機(jī)加載Class文件的過程是動(dòng)態(tài)連接的妒蔚,因此在Class文件中不會(huì)保存各個(gè)方法、字段的最終內(nèi)存布局信息月弛,這就需要虛擬機(jī)在運(yùn)行時(shí)從常量池獲得對(duì)應(yīng)的符號(hào)引用肴盏,再在類創(chuàng)建時(shí)或運(yùn)行時(shí)解析、翻譯到具體的內(nèi)存地址之中帽衙。
- 常量池中每一個(gè)常量都是一個(gè)表叁鉴,詳解見Class文件結(jié)構(gòu)--常量池(一)
d.訪問標(biāo)志
- 訪問標(biāo)志(access_flags):常量池結(jié)束后兩個(gè)字節(jié)
- 作用:識(shí)別一些類或者接口層次的訪問信息,包括該Class是類還是接口佛寿、是否定義為public類型、是否定義為abstract類型、若是類是否被聲明為final等冀泻。具體的標(biāo)志位以及含義見圖:
e.類索引常侣、父類索引與接口索引集合
- 類索引(this_class)和父類索引(super_class)都是一個(gè)u2類型的數(shù)據(jù)、接口索引集合(interfaces)是一組u2類型的數(shù)據(jù)的集合
- 作用: 通過這三項(xiàng)數(shù)據(jù)來確定這個(gè)類的繼承關(guān)系弹渔,具體的
- 類索引:確定這個(gè)類的全限定名
- 父類索引:確定這個(gè)類的父類的全限定名
- 接口索引集合:描述這個(gè)類所實(shí)現(xiàn)的接口胳施,并按照implements語句后的接口順序從左到右排列在接口索引集合中
- 接口索引集合的入口第一項(xiàng)u2類型數(shù)據(jù)為接口計(jì)數(shù)器(interfaces_count),從0計(jì)數(shù)肢专,如nterfaces_count=2表示該類實(shí)現(xiàn)了兩個(gè)接口
類全限定名:把類全名中的“.”都替換成“/”舞肆,為了使連續(xù)的多個(gè)全限定名之間不產(chǎn)生混淆,在使用時(shí)最后一般會(huì)加入一個(gè)“博杖;”表示全限定名結(jié)束
f.字段表集合
- 字段表(field_info):用于描述接口或者類中聲明的變量
- 格式如圖
- access_flags:存放字段的修飾符椿胯,具體的標(biāo)志位以及含義見圖:
- name_index:存放字段的簡(jiǎn)單名稱,即沒有類型和參數(shù)修飾的字段名稱
- descriptor_index:存放字段和方法的描述符剃根,包括字段的數(shù)據(jù)類型哩盲、方法的參數(shù)列表(包括數(shù)量、類型以及順序)和返回值狈醉,具體的標(biāo)志位以及含義見圖:
- attribute_info:屬性表見后
g.方法表集合
- 方法表(methods_info):用于描述接口或者類中聲明的方法
- 格式如圖廉油,可見和描述字段的方式非常類似,僅在訪問標(biāo)志和屬性表集合的可選項(xiàng)中有所區(qū)別苗傅。
h.屬性表集合
- 屬性表(attribute_info):用于描述某些場(chǎng)景專有的信息抒线,在字段表、方法表等都攜帶自己的屬性表集合
-
種類:
- 結(jié)構(gòu):屬性名需要從常量池中引用一個(gè)CONSTANT_Utf8_info類型的常量來表示渣慕,屬性值是自定義的嘶炭、需要通過一個(gè)u4的長度屬性說明屬性值所占用的位數(shù)
舉例:class文件結(jié)構(gòu)解析、class文件屬性表解析
3.字節(jié)碼指令
- 構(gòu)成:由一個(gè)字節(jié)長度的表示某種特定操作含義的操作(操作碼摇庙、Opcode)和零至多個(gè)代表此操作所需的參數(shù)(操作數(shù)旱物、Operands)構(gòu)成
- 特點(diǎn):非完全獨(dú)立,即并非每種數(shù)據(jù)類型和每一種操作都有對(duì)應(yīng)的指令卫袒,有些單獨(dú)的指令可以在必要的時(shí)候用來將一些不支持的類型轉(zhuǎn)換為可被支持的類型
- 分類:將字節(jié)碼操作按用途大致分為9類
- 加載和存儲(chǔ)指令:用于將數(shù)據(jù)在棧幀中的局部變量表和操作數(shù)棧之間來回傳輸
- 運(yùn)算指令:用于對(duì)兩個(gè)操作數(shù)棧上的值進(jìn)行某種特定運(yùn)算宵呛,并把結(jié)果重新存入到操作棧頂
- 類型轉(zhuǎn)換指令 :用于實(shí)現(xiàn)用戶代碼中的顯式類型轉(zhuǎn)換操作,或者用于處理字節(jié)碼指令集中數(shù)據(jù)類型相關(guān)指令無法與數(shù)據(jù)類型一一對(duì)應(yīng)的問題
- 對(duì)象創(chuàng)建與訪問指令:用于對(duì)象創(chuàng)建夕凝,并通過對(duì)象訪問指令獲取對(duì)象實(shí)例或者數(shù)組實(shí)例中的字段或者數(shù)組元素
- 操作數(shù)棧管理指令:用于直接操作操作數(shù)棧
- 控制轉(zhuǎn)移指令:用于從指定的位置有條件或無條件地進(jìn)行指令
- 方法調(diào)用和返回指令:用于方法的調(diào)用宝穗,并根據(jù)返回值的類型去返回
- 異常處理指令:用于檢測(cè)到異常狀況時(shí)自動(dòng)拋出異常
- 同步指令:用于方法內(nèi)部一段指令序列的同步
具體指令見Java虛擬機(jī)字節(jié)碼指令簡(jiǎn)介