1、概述java
虛擬機把描述類的數據從class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終造成能夠被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。安全
2、類加載的時機數據結構
類從被加載到虛擬機內存中開始,到卸載出內存爲止,它的整個生命週期包括:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸載(Unloading)7個階段。其中驗證、準備、解析3個部分統稱爲鏈接(Linking),順序以下圖,圖7-1 類的生命週期佈局
其中加載、驗證、準備、初始化和卸載這5個階段的順序是肯定的,類加載過程必須按照這種順序開始。性能
Java虛擬機規範中沒有對加載進行強制約束,由虛擬機的具體實現來自由把握。對於初始化階段,則有嚴格規定了有且只有五中狀況必須當即執行初始化:翻譯
一、遇到new、getstatic、putstatic或invokestatic這4條字節碼指令時,若是沒有進行初始化過,則須要先觸發其初始化。設計
二、使用java.lang.reflect包的方法對類進行反射調用的時候,若是類沒有進行過初始化,則須要先觸發其初始化。指針
三、當初始化一個類的時候,若是發現其父類還沒進行初始化,則須要先觸發其父類的初始化。對象
四、當虛擬機啓動時,用戶須要指定一個要執行的主類(包含main方法的那個類),虛擬機會先初始化這個主類。blog
五、當時用JDK1.7的動態語言支持時,若是一個java.lang.invoke.MethodHandle實例最後解析結果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,而且句柄所對應的類沒有進行初始化,則須要先觸發其初始化。
以上五種場景中的行爲稱爲對一個類的主動引用,除此以外,全部引用類的方法都不會觸發初始化,被稱爲被動引用。
3、類加載的過程
一、加載
加載是類加載過程的一個階段。在加載階段須要完成如下3件事情:
1)、經過一個類的全限定名來獲取定義此類的二進制字節流。
2)、將這個字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構。
3)、在內存中生成一個表明這個類的java.lang.Class對象,做爲方法區這個類的各類數據的訪問入口。
二、驗證
驗證是鏈接階段的第一步,這一階段的目的是爲了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,而且不會危害虛擬機自身的安全。驗證階段是很是重要的,這個階段是否嚴謹,直接決定了Java虛擬機是否能承受惡意代碼的攻擊,從執行性能的角度上講,驗證階段的工做量在虛擬機的類加載子系統中又佔了至關大的一部分。驗證大體會完成下面4個階段的檢驗動做:文件格式驗證、元數據驗證、字節碼驗證、符號運用驗證。
三、準備
準備階段是正式爲類變量分配內存並設置類變量初始值的階段,這些變量都將在方法區中進行分配。首先,這時候進行內存分配的僅包括類變量,而不包括實例變量,實例變量將會在對象實例化時隨着對象一塊兒分配在Java堆中。其次,這裏所說的初始化「一般狀況」下是數據類型的零值,假設一個類變量的定義爲:public static int value=123; 那麼變量value在準備階段事後的初始值爲0而不是123,將value賦值爲123的動做將在初始化階段纔會執行。
四、解析
解析階段是虛擬機將常量池內的符號引用替換爲直接引用的過程。
符號引用(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種常量類型。
五、初始化
類初始化階段是類加載過程的最後一步,這個階段才真正開始執行類中定義的Java程序代碼。
初始化階段是執行類構造器()方法的過程。
4、類加載器
虛擬機設計團隊把類加載階段中的「經過一個類的全限定名來獲取描述此類的二進制字節流」這個動做放到Java虛擬機外部去實現,以便讓應用程序本身決定如何去獲取所須要的類。實現這個動做的代碼模塊稱爲「類加載器」。
一、雙親委派模型
從Java虛擬機的角度講,只存在兩種不一樣的類加載器:一種是啓動類加載器(Bootstrap ClassLoader),是虛擬機的一部分;另外一種就是全部其餘類加載器,獨立於虛擬機外部,而且所有都繼承自抽象類java.lang.ClassLoader。
絕大部分Java程序都會使用到啓動類加載器(Bootstrap ClassLoader)、擴展類加載器(Extension ClassLoader)、應用程序類加載器(Application ClassLoader)這三個系統提供的類加載器。
雙親委派模型要求除了頂層的啓動類加載器外,其他的類加載器都應當有本身的父類加載器。這裏的類加載之間的父子關係通常不會以繼承(Inheritance)的關係來實現,而都是組合(Composition)關係來複用父加載器的代碼。圖7-2 類加載器雙親委派模型
雙親委派模型的工做過程是:若是一個類加載器收到了類加載的請求,它首先不會本身嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的類加載器都是如此,所以全部的加載請求最終都應該傳送到頂層的啓動類加載器中,只有當父加載器反饋本身沒法完成這個加載請求時,子加載器纔會嘗試本身去加載。
二、破壞雙親委派模型
本文來自於《深刻Java虛擬機-JVM高級特性與最佳實踐》---周志明。若是侵權,請聯繫做者刪除。