一、語言無關性與平臺無關性
語言無關性:Java虛擬機上運行的是Class文件(字節碼文件*.class),而Class文件不一定由Java程序編譯而來,JRuby經過jrubyc編譯器編譯生成的是Class文件,Groovy程序經過groovyc編譯器編譯後生成的也是class文件,都可在虛擬機上運行,虛擬機不關心Class的來源是何種語言。
平臺無關性:一次編寫,到處運行,各種不同平臺的虛擬機與所有平臺都統一使用的程序存儲格式——字節碼。
二、Class類文件的結構
以字節爲基礎單位:
Class文件是一組以8位字節爲基礎單位的二進制流,各數據項目嚴格按照順序緊湊地排列在Class文件之中,中間沒有添加任何分隔符。當遇到需要佔用8位字節以上空間的數據項時,則會按照高位在前的方式分割成若干個8位字節進行存儲。
兩種數據類型:無符號數和表
無符號數屬於基本的數據類型,u1表示1個字節,u2表示2個字節,u4代表4個字節。
表是由多個無符號數或者其他表作爲數據項構成的符合數據類型。
魔數:
Class文件的頭4個字節稱爲魔數,確定這個文件是否爲一個能被虛擬機接收的Class文件。值爲0xCAFEBABE。
版本號:
緊接着4個字節存儲的是Class文件的版本號。Minor Version(2字節)+Major Version(2字節)。
常量池:
緊接着版本號的是常量池入口,常量池可以理解爲Class文件之中的資源倉庫。
主要存放兩大類常量:字面量和符號引用。
字面量:文本字符串、聲明爲final的常量值等。
符號引用:類和接口的全限定名、字段的名稱和描述符、方法的名稱和描述。
Java代碼在進行編譯時,不像C和C++那樣有“連接”這一步驟,而是虛擬機加載Class文件的時候進行動態連接。
訪問標誌:
用於識別一些類或者接口層次的訪問信息,包括Class是類還是接口,是否爲public類型,是否abstract類型,是否爲final類型。
類索引、父類索引接口與接口索引集合:
類索引:u2類型的數據,指向一個類型爲CONSTANT_Class_info的類描述符常量,通過CONSTANT_Class_info類型中的索引值可以找到定義在CONSTANT_Utf8_info類型的常量中的全限定名字符串。
父類索引:u2類型,其他和上面一樣。
接口索引集合:因爲單個類可以實現多個接口,所以入口的第一項爲u2類型的接口計數器。後面是接口集合。
字段表集合:
用於描述接口或者類中聲明的變量。
字段的修飾符(有或無,布爾類型)用標誌位來表示。如public、static、final等
字段的名字、字段被定義爲什麼數據類型,這些無法固定,只能引用常量池的常量來表示。
字段表集合中不會列出從超類或者父接口中繼承而來的字段,但有可能列出原本Java代碼之中不存在的字段,譬如內部類爲了保持對外部類的訪問性,會自動添加指向外部類實例的字段。
方法表集合:
類似於字段表集合,包括訪問標誌、名稱索引、描述符索引、屬性表集合。
描述方法的定義,方法裏面的代碼存放在方法屬性表集合中一個名爲Code的屬性裏面。
其中<init>爲編譯器爲添加的的實例構造器。
如果父類方法在子類中沒有被重寫,方法表集合中就不會出現來自父類的方法信息。
要重載一個方法,除了要與原方法有相同的簡單名稱之外,要求有一個與原方法不同的特徵簽名。特徵簽名就是一個方法中各個參數在常量池中字段符號引用的集合,返回值不會包含在特徵簽名中,因此無法靠返回值的不同來對一個已有方法進行重載。
屬性表集合:
Class文件、字段表、方法表都可以攜帶自己的屬性表集合。
三、字節碼指令
1、字節碼指令長度
操作碼爲1個字節,不對齊,有利於提高傳輸效率。
2、字節碼與數據類型
iload指令用於從局部變量表中加載int型的數據到操作數棧。(fload則float)
虛擬機內部對不同數據類型的指令可能是同一種實現方式。
大部分指令都沒有支持整數類型byte,char和short,編譯器在編譯器或運行期將這些類型數據零位擴展爲響應的int類型數據。
3、加載和存儲指令
iload_<n>:將一個局部變量加載到操作棧。
istore_<n>:將一個數值從操作數棧存儲到局部變量表。
iconst<i>:將一個常量加載到操作數棧。
4、運算指令
5、類型轉換指令
6、對象創建與訪問指令
7、操作數棧管理指令
8、控制轉移指令
9、方法調用和返回指令
10、異常處理指令
11、同步指令