標籤:位元組流 put tst 檔案的 並且 img 分享 常量池 工作過程
一、概述
虛擬機器把描述類的資料從class檔案載入到記憶體,並對資料進行校正、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java類型,這就是虛擬機器的類載入機制。
二、類載入的時機
類從被載入到虛擬機器記憶體中開始,到卸載出記憶體為止,它的整個生命週期包括:載入(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸載(Unloading)7個階段。其中驗證、準備、解析3個部分統稱為串連(Linking),順序如,圖7-1 類的生命週期
其中載入、驗證、準備、初始化和卸載這5個階段的順序是確定的,類載入過程必須按照這種順序開始。
Java虛擬機器規範中沒有對載入進行強制限制式,由虛擬機器的具體實現來自由把握。對於初始化階段,則有嚴格規定了有且只有五中情況必須立即執行初始化:
1、遇到new、getstatic、putstatic或invokestatic這4條位元組碼指令時,如果沒有進行初始化過,則需要先觸發其初始化。
2、使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要先觸發其初始化。
3、當初始化一個類的時候,如果發現其父類還沒進行初始化,則需要先觸發其父類的初始化。
4、當虛擬機器啟動時,使用者需要指定一個要執行的主類(包含main方法的那個類),虛擬機器會先初始化這個主類。
5、當時用JDK1.7的動態語言支援時,如果一個java.lang.invoke.MethodHandle執行個體最後解析結果REF_getStatic、REF_putStatic、REF_invokeStatic的方法控制代碼,並且控制代碼所對應的類沒有進行初始化,則需要先觸發其初始化。
以上五種情境中的行為稱為對一個類的主動引用,除此之外,所有引用類的方法都不會觸發初始化,被稱為被動引用。
三、類載入的過程
1、載入
載入是類載入過程的一個階段。在載入階段需要完成以下3件事情:
1)、通過一個類的全限定名來擷取定義此類的二進位位元組流。
2)、將這個位元組流所代表的靜態儲存結構轉化為方法區的運行時資料結構。
3)、在記憶體中產生一個代表這個類的java.lang.Class對象,作為方法區這個類的各種資料的訪問入口。
2、驗證
驗證是串連階段的第一步,這一階段的目的是為了確保Class檔案的位元組流中包含的資訊符合當前虛擬機器的要求,並且不會危害虛擬機器自身的安全。驗證階段是非常重要的,這個階段是否嚴謹,直接決定了Java虛擬機器是否能承受惡意代碼的攻擊,從執行效能的角度上講,驗證階段的工作量在虛擬機器的類載入子系統中又佔了相當大的一部分。驗證大致會完成下面4個階段的檢驗動作:檔案格式驗證、中繼資料驗證、位元組碼驗證、符號運用驗證。
3、準備
準備階段是正式為類變數分配記憶體並設定類變數初始值的階段,這些變數都將在方法區中進行分配。首先,這時候進行記憶體配置的僅包括類變數,而不包括執行個體變數,執行個體變數將會在對象執行個體化時隨著對象一起分配在Java堆中。其次,這裡所說的初始化“通常情況”下是資料類型的零值,假設一個類變數的定義為:public static int value=123; 那麼變數value在準備階段過後的初始值為0而不是123,將value賦值為123的動作將在初始化階段才會執行。
4、解析
解析階段是虛擬機器將常量池內的符號引用替換為直接引用的過程。
符號引用(Symbolic References):符號引用以一組符號來描述引用所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。符號引用與虛擬機器實現的記憶體布局無關,引用的目標並不一定已經載入到記憶體中。各種虛擬機器實現的記憶體布局可以不相同,但是它們能接受的符號引用必須是一致的,因為符號引用的字面量形式明確定義在Java虛擬機器規範的Class檔案格式中。
直接引用:(Direct References):直接引用可以直接指向目標的指標、相對位移量或是一個能間接定位到目標的控制代碼。直接引用是和虛擬機器實現的記憶體布局相關的,同一個符號引用在不同虛擬機器執行個體上翻譯出來的直接引用一般不會相同。如果有了直接引用,那引用的目標必定在記憶體中存在。
解析動作主要針對類或者介面、欄位、類方法、介面方法、方法類型、方法控制代碼和調用點限定符7類符號引用進行,分別對應於常量池的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、CONSTANT_MethodType_info、CONSTANT_MethodHandle_info和CONSTANT_InvokeDynamic_info 7種常量類型。
5、初始化
類初始化階段是類載入過程的最後一步,這個階段才真正開始執行類中定義的Java程式碼。
初始化階段是執行類構造器()方法的過程。
四、類載入器
虛擬機器設計團隊把類載入階段中的“通過一個類的全限定名來擷取描述此類的二進位位元組流”這個動作放到Java虛擬機器外部去實現,以便讓應用程式自己決定如何去擷取所需要的類。實現這個動作的代碼模組稱為“類載入器”。
1、雙親委派模型
從Java虛擬機器的角度講,只存在兩種不同的類載入器:一種是啟動類載入器(Bootstrap ClassLoader),是虛擬機器的一部分;另一種就是所有其他類載入器,獨立於虛擬機器外部,並且全部都繼承自抽象類別java.lang.ClassLoader。
絕大部分Java程式都會使用到啟動類載入器(Bootstrap ClassLoader)、擴充類載入器(Extension ClassLoader)、應用程式類載入器(Application ClassLoader)這三個系統提供的類載入器。
雙親委派模型要求除了頂層的啟動類載入器外,其餘的類載入器都應當有自己的父類載入器。這裡的類載入之間的父子關係一般不會以繼承(Inheritance)的關係來實現,而都是組合(Composition)關係來複用父載入器的代碼。圖7-2 類載入器雙親委派模型
雙親委派模型的工作過程是:如果一個類載入器收到了類載入的請求,它首先不會自己嘗試載入這個類,而是把這個請求委派給父類載入器去完成,每一個層次的類載入器都是如此,因此所有的載入請求最終都應該傳送到頂層的啟動類載入器中,只有當父載入器反饋自己無法完成這個載入請求時,子載入器才會嘗試自己去載入。
2、破壞雙親委派模型
本文來自於《深入Java虛擬機器-JVM進階特性與最佳實務》---周志華。如果侵權,請聯絡作者刪除。
深入理解java虛擬機器(四)--類載入機制