1. 程式人生 > >一文讀懂類載入機制

一文讀懂類載入機制

類記載過程

多個java檔案經過編譯打包生成可執行的jar包,最終由java命令執行某個主類的main函式啟動程式,這裡首先需要通過類載入器把主類載入到jvm。
主類在執行過程中如果使用到其他類,會逐步載入這些類。
注意,jar包裡的類不是一次性全部載入的,是使用時才載入的。

從類載入到使用整個過程由如下幾步:
載入、驗證、準備、解析、初始化、使用、解除安裝

  • 載入:在硬碟上查詢並通過IO讀入位元組碼檔案,使用到類時才會載入,例如:呼叫類的main方法,new物件等;
  • 驗證:校驗位元組碼檔案的正確性;
  • 準備:給類的靜態變數分配記憶體,並賦予預設值;
  • 解析:將符號引用替換為直接引用,該階段會把一些靜態方法(符號引用,比如main方法)替換為指向資料所在記憶體的指標或控制代碼等(直接引用),這就是靜態連結過程,這個過程是在類記載期間完成的。動態連結是程式執行期間完成的將符號引用替換為直接引用。
  • 初始化:對類的靜態變數初始化為指定的值,執行靜態程式碼塊。

    類載入器

    上面的類載入過程主要是通過類載入器來實現的,java裡有以下幾種類載入器。
  • 啟動類載入器:負責載入支撐JVM執行的位於JREd額lib目錄下的核心類庫;
  • 擴充套件類載入器:負責載入支撐JVM執行位於JRE的lib目錄下的ext擴充套件目錄中的JAR類包;
  • 應用程式載入器:負責載入ClassPath路徑下的類包,主要就是載入應用程式的類;
  • 自定義載入器:負責載入使用者自定義路徑下的類包;

類記載器繼承了java.lang.ClassLoader類,該類有兩個核心方法,loadClass和findClass。

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

findClass方法預設實現是丟擲異常,所以我們在自定義類載入器主要是重寫findClass方法。

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

雙親委派機制

jvm類載入器是有親子層結構的.

這裡類載入就是雙親委派機制,記載某個類時, 會先委託父載入器尋找目標類,找不到再委託上層父載入器載入,如果所有父載入器再自己的載入類路徑下都找不到目標類,則再自己的類載入路徑中查詢並載入目標類。

比如:Math類,最先會找應用程式類載入器載入,應用程式載入器會先委託擴充套件類載入器載入,擴充套件類載入器再委託啟動類載入器,頂層啟動類載入器在自己的類載入器路徑裡找了半天沒找到Math類,則向下退回載入Math類的請求,擴充套件類載入器收到回覆就自己載入,在自己的類載入路徑裡找了半天也沒找到Math類,又向下退回Math類的載入請求給應用程式類載入器,應用程式類載入器於是在自己的類載入路徑中找到了Math類,於是就自己載入了。

雙親委派機制說簡單就是:先找父親載入,不行再由父親自己載入。

還沒關注我的公眾號?

  • 掃文末二維碼關注公眾號【小強的進階之路】可領取如下:
  • 學習資料: 1T視訊教程:涵蓋Javaweb前後端教學視訊、機器學習/人工智慧教學視訊、Linux系統教程視訊、雅思考試視訊教程;
  • 100多本書:包含C/C++、Java、Python三門程式語言的經典必看圖書、LeetCode題解大全;
  • 軟體工具:幾乎包括你在程式設計道路上的可能會用到的大部分軟體;
  • 專案原始碼:20個JavaWeb專案原始碼。