深入理解Java虛擬機器(二) 類載入機制,深入理解虛擬機器
Write Once Run Anywhere,一次編寫多處運行一、Java類檔案結構
為類檔案的結構,從上到下排序依次為:
- magic_number魔數:每個Class檔案的頭四個位元組稱為魔數,它的唯一作用就是確定這個檔案是否為一個能被虛擬機器接受的Class檔案。
- minor_version:次版本號碼
- major_version:主要版本號
- constant_pool_count:Class檔案中常量池的數目,由於常量池的計數從1開始,因此常量池的容量是constant_pool_count-1
- constant_pool:常量池是Class類檔案中出現的第一個表類型資料,常量池主要存放兩大類常量:a.字面量(Literal):包括文本字串、final類型常量值。b.符號引用(SymbolicReferences):包括類和介面的全限定名、欄位的名稱和描述符、方 法的名稱和描述符。
- access_flags:用於表示Class或介面層次的訪問標誌,即類或介面層面的存取控制資訊,通常儲存的資訊包括:Class類檔案是類、介面、枚舉或是註解;是否定義為public類型;是否定義為abstract類型;類是否被定義為final等等。
- this_class、super_class和interfaces:this_class類索引用於確定類的全限定名,super_class父類索引用於確定父類的全限定名,interfaces介面索引用於確定介面的全限定名,由於java中可以實現多個介面,因此使用interfaces_count來儲存介面數量。
field:field_info欄位表用於描述介面或者類中聲明的變數,field欄位包括了類級變數(靜態變數)和執行個體級變數(成員變數),但不包括方法內部的局部變數。
fields_count欄位數目表示Class檔案中的類和執行個體變數總數,欄位存放的資訊包括:欄位訪問標誌、是否靜態、是否final、是否並發可見volatile、是否可序列化transient、資料類型、欄位名稱等等。
注意:欄位表中不包含從父類或者介面中繼承而來的欄位,但是會添加原本代碼中不存在的欄位,例如this,以及內部類對外部類訪問而自動添加的外部類執行個體欄位等。
method:method_info方法表用於描述類或者介面中聲明的方法,methods_count用於表示Class檔案中方法總數,method方法儲存了方法的訪問標識、是否靜態、是否final、是否同步synchronized、是否本地方法native、是否抽象方法abstract、方法傳回值類型、方法名稱、方法參數列表等資訊。
方法的代碼指令並沒有直接存放在方法表中,而是存放著屬性工作表中的方法表Code中。
注意:如果父類的方法在子類沒有被重寫,方法表中不會出現來自父類的方法資訊,但是編譯器會自動添加類構造器”<clinit>”方法和執行個體構造器”<init>”方法。
Java編譯器的方法特徵簽名只包括:方法名稱、參數順序和參數類型,不包括方法傳回值類型,因此java的方法重載不能通過方法的傳回值類區別,但是在Class檔案中,方法特徵簽名包括方法的傳回值類型,因此Class檔案中可以共存兩個名稱和參數完全相同而傳回值類型不同的方法。
- attribute:attribute_info屬性工作表是Class檔案格式中最具擴充性的一種資料項目,用於存放field_info欄位表、method_info方法表以及Class檔案的專有資訊,屬性工作表不要求各個屬性有嚴格順序,只要求不與已有的屬性名稱字重複即可
二、類載入機制
在載入階段,java虛擬機器需要完成以下3件事:
a.通過一個類的全限定名來擷取定義此類的二進位位元組流。
b.將定義類的二進位位元組流所代表的靜態儲存結構轉換為方法區的運行時資料結構。
c.在java堆中產生一個代表該類的java.lang.Class對象,作為方法區資料的訪問入口。
Java虛擬機器的類載入是通過類載入器實現的, Java中的類載入器體繫結構如下:
- BootStrap ClassLoader:啟動類載入器,負責載入存放在%JAVA_HOME%\lib目錄中的,或者通被-Xbootclasspath參數所指定的路徑中的,並且被java虛擬機器識別的(僅按照檔案名稱識別,如rt.jar,名字不符合的類庫,即使放在指定路徑中也不會被載入)類庫到虛擬機器的記憶體中,啟動類載入器無法被java程式直接引用。
- Extension ClassLoader:擴充類載入器,由sun.misc.Launcher$ExtClassLoader實現,負責載入%JAVA_HOME%\lib\ext目錄中的,或者被java.ext.dirs系統變數所指定的路徑中的所有類庫,開發人員可以直接使用擴充類載入器。
- Application ClassLoader:應用程式類載入器,由sun.misc.Launcher$AppClassLoader實現,負責載入使用者類路徑classpath上所指定的類庫,是類載入器ClassLoader中的getSystemClassLoader()方法的傳回值,開發人員可以直接使用應用程式類載入器,如果程式中沒有自訂過類載入器,該載入器就是程式中預設的類載入器。
注意:上述三個JDK提供的類載入器雖然是父子類載入器關係,但是沒有使用繼承,而是使用了組合關係。
從JDK1.2開始,java虛擬機器規範推薦開發人員使用雙親委派模式(ParentsDelegation Model)進行類載入,其載入過程如下:
- 如果一個類載入器收到了類載入請求,它首先不會自己去嘗試載入這個類,而是把類載入請求委派給父類載入器去完成。
- 每一層的類載入器都把類載入請求委派給父類載入器,直到所有的類載入請求都應該傳遞給頂層的啟動類載入器。
- 如果頂層的啟動類載入器無法完成載入請求,子類載入器嘗試去載入,如果連最初發起類載入請求的類載入器也無法完成載入請求時,將會拋出ClassNotFoundException,而不再調用其子類載入器去進行類載入。
雙親委派 模式的類載入機制的優點是java類它的類載入器一起具備了一種帶優先順序的層次關係,越是基礎的類,越是被上層的類載入器進行載入,保證了java程式的穩定運行。