java類的加載過程
“加載”(Loading)階段是“類加載”(Class Loading)過程的第一個階段,在此階段,虛擬機需要完成以下三件事情:
1、 通過一個類的全限定名來獲取定義此類的二進制字節流。
2、 將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
3、 在Java堆中生成一個代表這個類的java.lang.Class對象,作為方法區這些數據的訪問入口。
加載階段即可以使用系統提供的類加載器在完成,也可以由用戶自定義的類加載器來完成。加載階段與連接階段的部分內容(如一部分字節碼文件格式驗證動作)是交叉進行的,加載階段尚未完成,連接階段可能已經開始。
驗證
驗證是連接階段的第一步,這一階段的目的是為了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。
Java語言本身是相對安全的語言,使用Java編碼是無法做到如訪問數組邊界以外的數據、將一個對象轉型為它並未實現的類型等,如果這樣做了,編譯器將拒絕編譯。但是,Class文件並不一定是由Java源碼編譯而來,可以使用任何途徑,包括用十六進制編輯器(如UltraEdit)直接編寫。如果直接編寫了有害的“代碼”(字節流),而虛擬機在加載該Class時不進行檢查的話,就有可能危害到虛擬機或程序的安全。
不同的虛擬機,對類驗證的實現可能有所不同,但大致都會完成下面四個階段的驗證:文件格式驗證、元數據驗證、字節碼驗證和符號引用驗證。
1、文件格式驗證,是要驗證字節流是否符合Class文件格式的規範,並且能被當前版本的虛擬機處理。如驗證魔數是否0xCAFEBABE;主、次版本號是否正在當前虛擬機處理範圍之內;常量池的常量中是否有不被支持的常量類型……該驗證階段的主要目的是保證輸入的字節流能正確地解析並存儲於方法區中,經過這個階段的驗證後,字節流才會進入內存的方法區中存儲,所以後面的三個驗證階段都是基於方法區的存儲結構進行的。
2、元數據驗證,是對字節碼描述的信息進行語義分析,以保證其描述的信息符合Java語言規範的要求。可能包括的驗證如:這個類是否有父類;這個類的父類是否繼承了不允許被繼承的類;如果這個類不是抽象類,是否實現了其父類或接口中要求實現的所有方法……
3、字節碼驗證,主要工作是進行數據流和控制流分析,保證被校驗類的方法在運行時不會做出危害虛擬機安全的行為。如果一個類方法體的字節碼沒有通過字節碼驗證,那肯定是有問題的;但如果一個方法體通過了字節碼驗證,也不能說明其一定就是安全的。
4、符號引用驗證,發生在虛擬機將符號引用轉化為直接引用的時候,這個轉化動作將在“解析階段”中發生。驗證符號引用中通過字符串描述的權限定名是否能找到對應的類;在指定類中是否存在符合方法字段的描述符及簡單名稱所描述的方法和字段;符號引用中的類、字段和方法的訪問性(private、protected、public、default)是否可被當前類訪問
驗證階段對於虛擬機的類加載機制來說,不一定是必要的階段。如果所運行的全部代碼確認是安全的,可以使用-Xverify:none參數來關閉大部分的類驗證措施,以縮短虛擬機類加載時間。
準備
準備階段是為類的靜態變量分配內存並將其初始化為默認值,這些內存都將在方法區中進行分配。準備階段不分配類中的實例變量的內存,實例變量將會在對象實例化時隨著對象一起分配在Java堆中。
public static int value=123;//在準備階段value初始值為0 。在初始化階段才會變為123 。
解析
解析階段是虛擬機將常量池內的符號引用替換為直接引用的過程。
符號引用(Symbolic Reference):符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。符號引用與虛擬機實現的內存布局無關,引用的目標並不一定已經加載到內存中。
直接引用(Direct Reference):直接引用可以是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。直接引用是與虛擬機實現的內存布局相關的,如果有了直接引用,那麽引用的目標必定已經在內存中存在。
初始化
類初始化是類加載過程的最後一步,前面的類加載過程,除了在加載階段用戶應用程序可以通過自定義類加載器參與之外,其余動作完全由虛擬機主導和控制。到了初始化階段,才真正開始執行類中定義的Java程序代碼。
初始化階段是執行類構造器<clinit>()方法的過程。<clinit>()方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊(static{}塊)中的語句合並產生的。
java類的加載過程