《Java 底層原理》Jvm 類的加載原理

前言

一直想好好的瞭解一下JVM,這次就來一起了解一下JVM是如何實現類的加載過程的。

原理

類加載的生命週期

1. 加載

5種類加載情況:

  • 在遇到 new、putstatic、getstatic、invokestatic 字節碼指令時,如果類尚未初始化,則需要先觸發初始化。
  • 對類進行反射調用時,如果類還沒有初始化,則需要先觸發初始化。
  • 初始化一個類時,如果其父類還沒有初始化,則需要先初始化父類。
  • 虛擬機啓動時,用於需要指定一個包含 main() 方法的主類,虛擬機會先初始化這個主類。
  • 當使用 JDK 1.7 的動態語言支持時,如果一個 java.lang.invoke.MethodHandle 實例最後的解析結果爲 REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄,並且這個方法句柄所對應的類還沒初始化,則需要先觸發初始化。

因爲Java類加載是不關心這個文件來自哪裏?

所有Java的對象可以來自Jar,war,網絡,CGlib,數據庫等等。

使用到對象纔會加載。

2. 驗證

  • 文件格式驗證
  • 元數據驗證
  • 字節碼驗證
  • 符號引用驗證

3. 準備

爲靜態變量分配內存、爲基礎數據類型賦初值,

如果被final修飾,在編譯的時候會給屬性添加ConstantValue屬性,準備階段直接完成賦值操作,無需賦初值。

4. 解析

間接引用轉直接引用。

間接引用:指向常量池。

直接引用:指向內存地址。

5. 初始化

執行靜態代碼段(靜態代碼塊,靜態變量):clinit

測試代碼寫了一個靜態變量b 通過字節碼文件也能清楚的看到信息。

靜態代碼的執行順序和定義的順序一致,改點可以通過案例說明。

public class JvmTest1 {

    public static void main(String[] args) {
        Test1 test1 = Test1.getTest();
        System.out.println(Test1.val1);
        System.out.println(Test1.val2);
    }
}

class Test1{
    public static int val1 ;

    Test1(){
        val1++;
        val2++;
    }

    public static Test1 instance = new Test1();

    public static int val2 = 1 ;    // 該代碼放在在初始化代碼

    public static Test1 getTest(){
        return instance;
    }
}

運行結果:

運行結果是1和1 ,造成這個結果的原因就是靜態代碼的順序執行,先創建了對象,對象中構造函數執行了, 後面再執行 public static int val2 = 1 ,值就被覆蓋回去了。

6. 使用

對象已經初始化完成,這個對象就可以再其他地方被使用。

7. 卸載

銷燬之前加載的對象信息。

Jvm裏面有一個類狀態的枚舉:

JVM類加載細節

1. jvm類加載會加鎖,防止類加載出現資源爭用的情況。

通過一個案例證明一下:

public class JvmTest3 {

    public static void main(String[] args) {
        new Thread(() -> {
            while(true){
                new AA();
            }

        }).start();

        new Thread(() -> {
            while(true) {
                new BB();
            }
        }).start();
    }
}

class AA{
    static {
        System.out.println("創建AA對象");
        new BB();
    }
}

class BB{
    static {
        System.out.println("創建BB對象");
        new AA();
    }
}

運行結果:

執行被鎖,無法繼續執行。

2. Jvm加載對象屬於懶加載(對象沒有使用的時候,不會去加載該對象),下面通過案例說明:

public class JvmTest2 {

    public static void main(String[] args) {
        System.out.println(B.str);
    }
}

class A {
    static String str = "a";
    static {
        System.out.println("static a");
    }
}

class B extends A {
    static {
        System.out.println("static b");
    }
}

運行結果:

可以明顯的看到B的static方法沒有被執行,因爲這段邏輯中,不需要是到B這個對象,哪怕B對象是A對象的子類。

總結

Jvm 類加載是核心邏輯之一,非常重要,對一個變成人員的提升也是非常的有幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章