概述:java
規範而獨立的類文件結構以及只與類文件關聯的虛擬機爲Java實現了平臺無關性,甚至還帶來了一些語言無關性。佈局
只要將源代碼編譯爲Class文件規定的格式,JVM就能夠執行。翻譯
JVM的指令描述能力比Java更強,這使得JVM能夠執行不一樣於Java語言特性的語言。3d
一、Class文件總體結構調試
以字節爲基本單位,無分隔符,大端(低地址存高位)。code
不管是數量仍是順序都嚴格規定了(肯定性)。對象
兩種數據類型:無符號數和表blog
一、無符號數:u一、u二、、u四、u8分別表明一、二、四、8字節的無符號數。可用來描述數字、索引引用、數量值或UTF-8字符串。繼承
二、表:由多個無符號數或者表組成。整個Class文件可看做是一張表。索引
三、數量不肯定時,必須有一個前置的數量說明。
二、Magic Number和版本號
魔數:標識該文件是一個Class文件。
版本號:Minor Version和Major Version,標識該Class文件的版本(能執行該Class文件的JVM最低版本)。
Java版本號從45開始,每個JDK大版本發佈,主版本號加1。
三、常量池
能夠理解爲Class文件中的資源倉庫,一般是Class文件中最佔空間的一部分。被其餘部分所引用,常量池元素之間也存在相互引用。就如同搭積木同樣。
(只有)常量池計數從1開始,0留出是爲了表示不引用任何常量池元素的狀況。
兩大類型常量:
一、字面量Literal:基本類型和字符串。
二、符號引用(引用其餘常量池元素)Symbolic Reference:
一、類和接口的全限定名;
二、字段的名稱和描述符;
三、方法的名稱和描述符。
Java編譯過程沒有連接步驟,Class文件沒有保存各個方法、字段在內存中的佈局信息,要依靠虛擬機加載Class文件的時候進行動態連接。
虛擬機運行時,須要從常量池中得到對應的符號引用,再在類建立或運行時解析、翻譯到具體的內存地址之中。
四、訪問標誌
經過標誌位用兩個字節表示。
五、類索引、父類索引和接口索引集合
這三個索引用於肯定類的繼承關係。
類索引、父類索引和接口索引集合按順序排列在訪問標誌以後,其中類索引和父類索引各用一個u2類型的索引值表示,指向常量池中的一個CONSTANT_Class_info元素。
因爲能夠實現多個接口,所以,要增長一個接口計數器,隨後緊接着接口索引集合。
接口索引一樣用一個u2類型的索引值表示,指向常量池中的一個CONSTANT_Class_info元素。
六、字段表集合
name_index引用常量池中的元素,爲字段的簡單名。
descriptor_index引用常量池中的元素,爲字段的描述符(包括數據類型)。
描述符用於描述字段的數據類型,方法的參數列表(包括數量、類型、順序)和返回值。
因爲字段(方法)名已經有單獨的屬性描述了,所以描述符裏再也不包括。
描述方法時,按照先參數列表,後返回值的順序。參數列表嚴格按照參數順序放在「()」以內。void 對應的描述符爲V。
描述符示例:
F: int i; D: I
F: java.lang.String[][] S; D: [[Ljava/lang/String
M: void inc(); D: ()V
M: double add(double x, double y); D: (DD)D
M: int add(int[] array); D: ([I)I
M: int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex);
D: ([CII[CIII)I
字段表不會包含繼承而來的字段,但可能包含Java代碼中沒有的字段(如內部類中包含外部類的引用)。
七、方法表集合
方法表的結構和字段表同樣,只是每一個屬性的具體內容有所變化(如訪問字段變了)。
若是子類方法表不會包含父類方法,除非子類重寫了那個方法。
可能出現代碼中沒有的方法,如編譯器添加的類構造器<cinit>方法和實例構造器<init>方法。
Java中特徵簽名就是一個方法中各個參數在常量池中的字段符號引用的集合,返回值不包含在特徵簽名中,所以Java沒法經過返回值區分方法重載。
可是在Class文件格式中,特徵簽名還包括方法返回值和受查異常表,所以只有返回值不一樣的兩個方法也能在Class文件中共存。
編譯後的字節碼去哪兒了?
在方法屬性表集合中的Code屬性裏面。
八、屬性表集合
屬性表都有固定的兩項
一、u2類型的attribute_name_index,指向常量池中CONSTANT_Utf8_info 型常量的索引,表明屬性名。
二、u4類型的attribute_length,表示後面的attribute_info的數量。
一、code屬性-方法表
javac編譯器編譯處理後的字節碼指令,一個字節表示一個指令。
並不是全部方法都存在Code屬性,抽象方法就沒有。
exception_table:
二、Exceptions屬性-方法表
與Code屬性平級的屬性,不是exception_table !
三、LineNumberTable屬性-Code屬性
描述Java源碼與字節碼偏移量的對應關係,用於調試。
可在編譯時加 -g:none 或 -g:lines 參數關閉。
四、LocalVariableTable屬性-Code屬性
描述棧幀中局部變量表與Java源碼中定義的變量之間的關係。
可在編譯時加 -g:none 或 -g:vars 參數關閉。
五、SourceFile屬性-類文件
記錄Class文件對應的源碼文件的名稱。
可在編譯時加 -g:none 或 -g:source 參數關閉。
六、ConstantValue屬性-字段表
記錄編譯時常量的值。
七、InnerClasses 屬性
記錄內部類與宿主類的關聯。
八、Deprecated和Synthetic屬性
Deprecated相似於@Deprecated。
Synthetic表示此字段或方法不是Java源碼直接生成的。
九、StackMapTable 屬性
用於類加載時的類型檢查。
十、Signiture屬性
用於在擦除法後獲取泛型的信息。
十一、BootstrapMethods屬性
用於保存invokedynamic指令引用的引導方法限定符。
九、字節碼指令簡介
Java字節碼指令和C/C++彙編不太同樣,它沒有寄存器的概念。而是採用了操做數棧來進行數據的傳遞和運算。
同時,一個字節序列就是一個指令。所以最多隻能有256個指令。所以,不是每種類型數據都有對應的運算指令。。
能夠分爲:
一、加載和存儲指令
二、運算指令
三、類型轉換指令
四、對象建立與訪問指令
五、操做數棧管理指令
六、控制轉移指令
七、方法調用和返回指令
八、異常處理指令
九、同步指令