Class 文件須要加載到虛擬機中以後才能運行和使用,那麼虛擬機是如何加載這些 Class 文件呢?html
系統加載 Class 類型的文件主要三步:加載->鏈接->初始化。鏈接過程又可分爲三步:驗證->準備->解析。java
類加載過程的第一步,主要完成下面3件事情:git
虛擬機規範多上面這3點並不具體,所以是很是靈活的。好比:"經過全類名獲取定義此類的二進制字節流" 並無指明具體從哪裏獲取、怎樣獲取。好比:比較常見的就是從 ZIP 包中讀取(往後出現的JAR、EAR、WAR格式的基礎)、其餘文件生成(典型應用就是JSP)等等。github
一個非數組類的加載階段(加載階段獲取類的二進制字節流的動做)是可控性最強的階段,這一步咱們能夠去完成還能夠自定義類加載器去控制字節流的獲取方式(重寫一個類加載器的 loadClass()
方法)。數組類型不經過類加載器建立,它由 Java 虛擬機直接建立。數組
類加載器、雙親委派模型也是很是重要的知識點,這部份內容會在後面的文章中單獨介紹到。安全
加載階段和鏈接階段的部份內容是交叉進行的,加載階段還沒有結束,鏈接階段可能就已經開始了。數據結構
準備階段是正式爲類變量分配內存並設置類變量初始值的階段,這些內存都將在方法區中分配。對於該階段有如下幾點須要注意:多線程
public static int value=111
,那麼 value 變量在準備階段的初始值就是 0 而不是111(初始化階段纔會複製)。特殊狀況:好比給 value 變量加上了 fianl 關鍵字public static final int value=111
,那麼準備階段 value 的值就被複製爲 111。基本數據類型的零值:oracle
解析階段是虛擬機將常量池內的符號引用替換爲直接引用的過程。解析動做主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用限定符7類符號引用進行。jvm
符號引用就是一組符號來描述目標,能夠是任何字面量。直接引用就是直接指向目標的指針、相對偏移量或一個間接定位到目標的句柄。在程序實際運行時,只有符號引用是不夠的,舉個例子:在程序執行方法時,系統須要明確知道這個方法所在的位置。Java 虛擬機爲每一個類都準備了一張方法表來存放類中全部的方法。當須要調用一個類的方法的時候,只要知道這個方法在方發表中的偏移量就能夠直接調用該方法了。經過解析操做符號引用就能夠直接轉變爲目標方法在類中方法表的位置,從而使得方法能夠被調用。
綜上,解析階段是虛擬機將常量池內的符號引用替換爲直接引用的過程,也就是獲得類或者字段、方法在內存中的指針或者偏移量。
初始化是類加載的最後一步,也是真正執行類中定義的 Java 程序代碼(字節碼),初始化階段是執行類構造器 <clinit> ()
方法的過程。
對於<clinit>()
方法的調用,虛擬機會本身確保其在多線程環境中的安全性。由於 <clinit>()
方法是帶鎖線程安全,因此在多線程環境下進行類初始化的話可能會引發死鎖,而且這種死鎖很難被發現。
對於初始化階段,虛擬機嚴格規範了有且只有5中狀況下,必須對類進行初始化:
java.lang.reflect
包的方法對類進行反射調用時 ,若是類沒初始化,須要觸發其初始化。參考