jvm - 類的加載

jvm - 類的編譯,提到了把本地機器碼轉變成字節碼,以及編譯的優化。那這些字節碼文件是怎麼到JVM的呢?
image.png
一個類從被加載到虛擬機內存中開始,到卸載出內存,它的整個生命週期包括:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸載(Unloading)7個階段。
image.png
在Java虛擬機中類加載的全過程,包括加載、驗證、準備、解析和初始化這5個階段所執行的具體動做,這些都是有類加載器來實現的。java

類加載

加載

加載是類加載過程的一個階段。首先來一個簡單的代碼,打印###以及建立一個Hello對象。安全

public class ClassLoad {
    public static void main(String[] args) {
        System.out.println("########################");
        Hello hello = new Hello();
    }
}

運行以前,設置-XX:+TraceClassLoading
image.png
運行結果以下(截取後面部分),能夠看到com.jvm.load.ClassLoad先被加載,而後是com.jvm.cls.Hello。ClassLoad是這個main方法的主類,因此優先加載。Hello的加載,是在實例化的時候,也就是被用到的時候,若是讀者本身去斷點,那就更直觀的看到了。
image.png
上面這個圖,能夠看到輸出了類的全限定名,類加載器就是經過這個來獲取它的二進制字節流,這個二進制字節流來源以下:網絡

  • class文件
  • zip、jar、war包中讀取
  • 網絡中讀取,好比Applet
  • 運行時計算生成,好比動態代理技術
  • 由其餘文件生成,好比JSP

image.png

驗證

驗證是爲了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,而且不會危害虛擬機自身的安全。當加載的class文件不符合虛擬機的要求,java虛擬機是沒法執行這個字節碼的,因此要先看看有沒有符合,符合了纔給虛擬機執行後續操做。
image.pngjvm

準備

準備是正式爲類變量分配內存並設置類變量初始值的階段。也就是說com.jvm.load.ClassLoadcom.jvm.cls.Hello在虛擬機中的內存分配是在這個階段。這時候進行內存分配的僅包括類變量(被static修飾的變量),而不包括實例變量,實例變量將會在對象實例化時隨着對象一塊兒分配在Java堆中。設置類變量初始值一般狀況下就是數據類型的零值。優化

// 準備階段value=0
public static int value = 123;
// 準備階段value2=123
public static final int value2 = 123;

image.png

解析

解析是虛擬機將常量池內的符號引用替換爲直接引用的過程。
好比com.jvm.load.ClassLoad編譯的時候,不知道com.jvm.cls.Hello的實際內存地址,此時用符號引用,當com.jvm.cls.Hello加載到內存後,此時就改成直接引用,指向Hello的內存位置。
image.pngspa

初始化

在準備階段value=0,在初始化階段,value才賦值爲123。
類初始化的條件:代理

  1. new一個對象,靜態變量的賦值和取值,靜態方法的調用。
  2. 經過反射機制調用。
  3. 若是子類初始化的時候,父類未初始化。
  4. 執行的主類(main方法)的時候。

下面看看類雖然被加載,卻沒有初始化的例子。
SuperClass:code

public class SuperClass {
    static {
        System.out.println("SuperClass init");
    }
    public static int value = 123;
}

SubClass:對象

public class SubClass extends SuperClass {
    static {
        System.out.println("SubClass init");
    }
}

ClassLoad:blog

public class ClassLoad {
    public static void main(String[] args) {
        System.out.println("########################");
        //Hello hello = new Hello();
 System.out.println(SubClass.value);
    }
}

運行結果以下:
image.png
能夠看到SubClass被加載了,可是並無輸出SubClass init
image.png

類加載器

類加載器有這幾個:

  • 啓動類加載器:jvm啓動的時候,會優先加載<JAVA_HOME>\lib這個目錄的核心類庫。
  • 擴展類加載器:負責加載<JAVA_HOME>\lib\ext這個目錄的類。
  • 應用程序類加載器:負責加載咱們寫的代碼。
  • 自定義類加載器:根據咱們的須要,加載特定的類。

下圖展現了類加載器直接的層次關係,成爲類加載器的雙親委派模型。雙親委派模型要求除了頂層的啓動類加載器外,其他的類加載器都應當有本身的父類加載器。
image.png
他的工做過程是這樣的:

  1. 應用程序類加載器收到了Hello類的加載請求,先問擴展類加載器是否能夠加載。
  2. 擴展類加載器也不會直接去加載,他也是向上級啓動類加載器詢問是否能夠加載。
  3. 啓動類加載器在本身負責的目錄搜索了一下,發現本身找不到這個類,就說不行,你本身加載吧。
  4. 擴展類加載器在本身負責的目錄搜索了一下,發現本身找不到這個類,就說不行,你本身加載吧。
  5. 應用程序類加載器在本身負責的目錄搜索了一下,找到了這個類,把Hello類加載進來。

image.png雙親委派模型一個顯而易見的好處就是Java類隨着它的類加載器一塊兒具有了一種帶有優先級的層次關係。

相關文章
相關標籤/搜索