Java中的JVM虛擬機的類加載機制

虛擬機的類加載機制

這篇文章是學習《深入理解JAVA虛擬機》中的一些個人認爲比較重要的知識點的總結

關於類的初始化

類的生命週期: 加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用和卸載(Using and Unloading)

企業微信截圖_15725942678682.png

  1. 加載:獲取類的二進制字節流

  2. 驗證:確保Class文件的字節流中包含的信息符合當前虛擬機的要求

  3. 準備:爲類變量分配內存並設置類變量初始值(不包含實例變量)

  4. 解析:將常量池內的符號引用替換爲直接引用的過程

  5. 初始化:類加載過程的最後一步,開始執行類中定義的Java程序代碼

  6. 使用:

  7. 卸載:

5種觸發類進行初始化的場景:

  1. new(使用new初始化一個類)、getStatic\putStatic(獲取或設置一個類的靜態字段)、invokeStatic(調用一個類的靜態方法)

  2. 使用java.lang.reflect 包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要先出發其初始化

  3. 初始化一個類的時候,如果發現其父類還沒進行過初始化,則需要先觸發其父類的初始化

  4. 虛擬機啓動時,用戶需要指定一個要執行的(包含main()方法的那個類),虛擬機會先初始化這個主類

其中一個demo

public class NotInitialization1 {
    public static void main(String[] args) {
        /**
         * 例子一:
         * 1. 讀取非final修飾的靜態字段時,如果類未被初始化則去初始化
         * 2. 當初始化一個類的時候,如果發現父類還未被初始化,則需要先出發父類的初始化
         * 3. 通過子類來引用父類中定義的靜態字段,只會觸發父類的初始化而不會觸發子類的初始化
         * @param args
         */
        System.out.println(SubClass.value);

    }
}


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

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

}
運行結果:
SuperClass init!
123

關於類加載器 Classloader

虛擬機設計團隊把類加載階段中“通過一個類的名稱來獲取描述此類的二進制字節流”,放到虛擬機外部去實現。這個代碼模塊成爲“類加載器”。

注意: 即使兩個類來源於同一個Class文件,被同一個虛擬機加載,只要加載它們的類加載器不同,那麼這兩個類必定不相等。

import java.io.IOException;
import java.io.InputStream;

/**
 * 示例:
 *    即使這兩個類都來源於同一個Class文件,但是屬於兩個不同的類加載器
 *    一個是由系統應用程序類加載器加載的,另一個由自定義的類加載器
 *
 * 結論:
 *    即使兩個類來源於同一個Class文件,被同一個虛擬機加載,只要加載它們的類加載器不同,那麼這兩個類必定不相等
 *
 */
public class ClassLoaderTest {

    public static void main(String[] args) throws Exception {

        //自定義一個類加載器
        ClassLoader myLoader = new ClassLoader() {
            /**
             * Loads the class with the specified <a href="#name">binary name</a>.
             * This method searches for classes in the same manner as the {@link
             * #loadClass(String, boolean)} method.  It is invoked by the Java virtual
             * machine to resolve class references.  Invoking this method is equivalent
             * to invoking {@link #loadClass(String, boolean) <tt>loadClass(name,
             * false)</tt>}.
             *
             * @param name The <a href="#name">binary name</a> of the class
             * @return The resulting <tt>Class</tt> object
             * @throws ClassNotFoundException If the class was not found
             */
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {

                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";

                    InputStream is = getClass().getResourceAsStream(fileName);

                    if (is == null) {
                        return super.loadClass(name);
                    }

                    byte[] b = new byte[0];

                    b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };

        //用自定義的類加載器去加載一個類
        Object obj = myLoader.loadClass("java8.classloading.classloader.ClassLoaderTest").newInstance();

        System.out.println(obj.getClass());

        //與相對路徑的類相對比
        System.out.println(obj instanceof java8.classloading.classloader.ClassLoaderTest);

    }
}
 運行結果:
  class java8.classloading.classloader.ClassLoaderTest
  false

關於雙親委派模型(Parents Delegation Model)

3種類加載器

  • 啓動類加載器(Bootstrap ClassLoader
  1. 這個類加載器使用C++語言實現,是虛擬機自身的一部分;

  2. 無法被Java程序直接引用

  3. 負責把類庫加載到虛擬機內存中

    1. 放在<JAVA_HOME>\lib目錄中

    2. 或者被 -Xbootclasspath 參數所指定的路徑中的

    3. 被虛擬機識別的(按照文件名識別,如rt.jar)

  • 擴展類加載器(Extension ClassLoader)
  1. 由sun.misc.Launcher$ExtClassLoader實現

  2. 開發者可以直接使用

  3. 負責加載<JAVA_HOME>\lib\ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫

  • 應用程序類加載器(Application ClassLoader)
  1. 由sun.misc.Launcher$App-ClassLoader實現

  2. 一般也稱爲系統加載器。程序中的默認類加載器

  3. 加載用戶類路徑(ClassPath)上所指定的類庫

什麼是雙親委派模型

undefined

如圖所示:類加載器之間的這種層次關係,稱爲類加載器的雙親委派模型

  • 工作流程:

如果一個類加載器收到了類加載的請求,先把請求委派給父類加載器去完成,每層如此;只有父加載器反饋自己無法完成這個加載請求(它的搜索範圍中沒有找到所需的類),子加載器纔會嘗試自己去加載

  • 好處:

保證程序裏各類加載器環境中都是同一個類,對於保證Java程序的穩定運作很重要

(代碼集中在java.lang.ClassLoader的loadClass()方法中)

題外

  1. 熱部署: 其實是通過自定義類加載機制實現了,其破壞了雙親委派模型(OSGI:Java模塊化標準)

  2. 關於反射: 提供很多方法,可以直接通過(類名.方法)的形式獲取類的相關信息(變量、方法等)

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