文章內(nèi)存摘抄自《深入理解Java虛擬機》,在此僅作學(xué)習(xí)記錄。
代碼編譯的結(jié)果從本地機器碼轉(zhuǎn)變?yōu)樽止?jié)碼(Byte Code),是存儲格式發(fā)展的一小步谎砾,卻是編程語言發(fā)展的一大步。
存儲格式發(fā)展:
今天的計算機仍然只能識別0和1捧颅,但將我們編寫的程序編譯成二進制本地機器碼(Native Code)已不再是唯一的選擇景图,越來越多的程序語言選擇了與操作系統(tǒng)和機器指令集無關(guān)的、 平臺中立的格式作為程序編譯后的存儲格式碉哑。
各種不同平臺的虛擬機與所有平臺都統(tǒng)一使用的程序存儲格式——字節(jié)碼(ByteCode) 是構(gòu)成平臺無關(guān)性的基石挚币,而且語言無關(guān)性正越來越被開發(fā)者所重視亮蒋。
Java虛擬機不和包括Java在內(nèi)的任何語言綁定,它只與“Class文件”這種特定的二進制文件格式所關(guān)聯(lián)妆毕,Class文件中包含了Java虛擬機指令集和符號表以及若干其他輔助信息慎玖。
Class類文件結(jié)構(gòu):
結(jié)構(gòu):
任何一個Class文件都對應(yīng)著唯一一個類或接口的定義信息,但反過來說笛粘,類或接口并不一定都得定義在文件里(譬如類或接口也可以通過類加載器直接生成)趁怔。文中只是通俗地將任意一個有效的類或接口所應(yīng)當(dāng)滿足的格式稱為“Class文件格式”,實際上它并不一定以磁盤文件的形式存在薪前。
Class文件是一組以8位字節(jié)為基礎(chǔ)單位的二進制流润努,各個數(shù)據(jù)項目嚴(yán)格按照順序緊湊地排列在Class文件之中,中間沒有添加任何分隔符序六,這使得整個Class文件中存儲的內(nèi)容幾乎 全部是程序運行的必要數(shù)據(jù)任连,沒有空隙存在。當(dāng)遇到需要占用8位字節(jié)以上空間的數(shù)據(jù)項 時例诀,則會按照高位在前[1]的方式分割成若干個8位字節(jié)進行存儲。
Class文件格式采用一種類似于C語言結(jié)構(gòu)體的偽結(jié)構(gòu)來存儲數(shù)據(jù)裁着,這種偽結(jié)構(gòu)中只有兩種數(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)成的復(fù)合數(shù)據(jù)類型,所有表都習(xí)慣性地 以“_info”結(jié)尾棘捣。
組成:
①魔數(shù)(Magic Number): 每個Class文件的頭4個字節(jié)稱為魔數(shù)辜腺,它的唯一作用是確定這個文件 是否為一個能被虛擬機接受的Class文件。
②版本號?: 第5和第6個字節(jié)是次版本號(Minor Version)乍恐,第7和第8個字節(jié)是主版本號(Major Version)评疗。
③常量池?:?緊接著主次版本號之后的是常量池入口,常量池可以理解為Class文件之中的資源倉庫茵烈, 它是Class文件結(jié)構(gòu)中與其他項目關(guān)聯(lián)最多的數(shù)據(jù)類型百匆,也是占用Class文件空間最大的數(shù)據(jù)項目之一,同時它還是在Class文件中第一個出現(xiàn)的表類型數(shù)據(jù)項目呜投。
常量池中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)加匈。
字面量?: 比較接近于Java語言層面的常量概念寄症,如文本字符串、聲明為final的常量值等矩动。
符號引用?: 則屬于編譯原理方面的概念有巧,包括了下面三類常量:
1.類和接口的全限定名(Fully Qualified Name)
2.字段的名稱和描述符(Descriptor)
3.方法的名稱和描述符
④訪問標(biāo)志?:?在常量池結(jié)束之后,緊接著的兩個字節(jié)代表訪問標(biāo)志(access_flags)悲没,這個標(biāo)志用于識 別一些類或者接口層次的訪問信息篮迎,包括:這個Class是類還是接口;是否定義為public類 型示姿;是否定義為abstract類型甜橱;如果是類的話,是否被聲明為final等栈戳。
⑤類索引岂傲、父類索引和接口索引集合?: 都按順序排列在訪問標(biāo)志之后.
類索引(this_class)和父類索引(super_class)都是一個u2類型的數(shù)據(jù),接口索引集合(interfaces)是一組u2類型的數(shù)據(jù)的集合.
類索引?: 用于確定這個類的全限定名
父類索引?: 用于確定這個類的父類的全限定名子檀。由于Java語言不允許多重繼承镊掖,所以父類索引只有一個,除了java.lang.Object之外褂痰,所有的Java 類都有父類亩进,因此除了java.lang.Object外,所有Java類的父類索引都不為0缩歪。
接口索引集合?: 就用來描述這個類實現(xiàn)了哪些接口归薛,這些被實現(xiàn)的接口將按implements語句
⑥字段表集合 :用于描述接口或者類中聲明的變量。
字段(field)包括類級變量以及實例級變量匪蝙,但不包括在方法內(nèi)部聲明的局部變量主籍。我們可以想一想在Java中描述一個字 段可以包含什么信息?可以包括的信息有:字段的作用域(public逛球、private千元、protected修飾 符)、是實例變量還是類變量(static修飾符)需忿、可變性(final)诅炉、并發(fā)可見性(volatile修飾 符,是否強制從主內(nèi)存讀寫)屋厘、可否被序列化(transient修飾符)涕烧、字段數(shù)據(jù)類型(基本類 型、對象汗洒、數(shù)組)议纯、字段名稱。
字段表集合中不會列出從超類或者父接口中繼承而來的字段溢谤,但有可能列出原本Java代 碼之中不存在的字段瞻凤,譬如在內(nèi)部類中為了保持對外部類的訪問性憨攒,會自動添加指向外部類 實例的字段。另外阀参,在Java語言中字段是無法重載的肝集,兩個字段的數(shù)據(jù)類型、修飾符不管是 否相同蛛壳,都必須使用不一樣的名稱杏瞻,但是對于字節(jié)碼來講,如果兩個字段的描述符不一致衙荐, 那字段重名就是合法的捞挥。
⑦方法表集合?: 用于對方法的描述,與對字段的描述幾乎采用了完全一致的方式.
訪問標(biāo)志(access_flags)忧吟、名稱索引(name_index)砌函、描述符索引(descriptor_index)、
屬性表集合(attributes):?方法里的Java代碼溜族,經(jīng)過編譯器編譯成字節(jié) 碼指令后讹俊,存放在方法屬性表集合中一個名為“Code”的屬性里面
⑧屬性表集合 :?在Class文件、字段表斩祭、方 法表都可以攜帶自己的屬性表集合劣像,以用于描述某些場景專有的信息。
字節(jié)碼指令 :?
Java虛擬機的指令由一個字節(jié)長度的摧玫、代表著某種特定操作含義的數(shù)字(稱為操作碼,Opcode)以及跟隨其后的零至多個代表此操作所需參數(shù)(稱為操作數(shù)绑青,Operands)而構(gòu)成诬像。
字節(jié)碼指令集是一種具有鮮明特點、優(yōu)劣勢都很突出的指令集架構(gòu)闸婴,由于限制了Java虛 擬機操作碼的長度為一個字節(jié)(即0~255)坏挠,這意味著指令集的操作碼總數(shù)不可能超過256 條;又由于Class文件格式放棄了編譯后代碼的操作數(shù)長度對齊邪乍,這就意味著虛擬機處理那些超過一個字節(jié)數(shù)據(jù)的時候降狠,不得不在運行時從字節(jié)中重建出具體數(shù)據(jù)的結(jié)構(gòu),如果要將一個 16位長度的無符號整數(shù)使用兩個無符號字節(jié)存儲起來(將它們命名為byte1和byte2)庇楞。