1. 程式人生 > 實用技巧 >JAVA ClassLoader類載入機制

JAVA ClassLoader類載入機制

參考文章:https://javasec.org/javase/ClassLoader/

關於類的載入機制,結果javasec和自己的理解來記錄的一篇文章!!!

概念的學習

首先得知道的兩點:

第一點:

Java是一個依賴於JVM(Java虛擬機器)實現的跨平臺的開發語言

第二點:類例項化的工作流程:

1、Java程式在執行前需要先編譯成class檔案(位元組碼),這個class檔案其實就是我們的javac生成的!

2、Java類初始化的時候會呼叫ClassLoader物件(這裡的ClassLoader其實也是一個類)載入類位元組碼(也就是class檔案)

3、ClassLoader會呼叫JVM的native方法(defineClass0/1/2)來定義一個java.lang.Class例項。

現在知道了兩個概念:

1、JAVA的執行依賴於JAVA虛擬機器

2、類的初始化需要ClassLoader物件

3、想要生成對應的類例項就需要ClassLoader去載入對應類的位元組碼(也就是class檔案)

注意:這裡說的類例項,並不是如Unsafe unsafe = new Unsafe()這裡面生成的unsafe例項,此unsafe例項跟這裡的類例項不同的,為什麼?

就比如這一句話:ClassLoader.getSystemClassLoader().loadClass("java.lang.String");,看著意思就是一個ClassLoader類中的getSystemClassLoader方法返回了一個物件然後呼叫該物件的loadClass方法載入了一個名為java.lang.String類,那麼這裡其實就知道了主要載入類的方法就是loadClass

可以觀察下該loadClass返回的是什麼,如下,那麼也就知道了這是一個名叫Class型別的物件,那麼也就是返回的是一個Class型別的物件,那麼也就證實了上面說的,unsafe例項跟這裡的類例項不同!

ClassLoader的介紹

觀察如下一張圖:

1、可以看到ClassLoader是位於JVM裡面的

2、所有的類的例項化都需要經過ClassLoader

總結:上面的圖中可以看到一切的Java類都必須經過JVM載入後才能執行,而ClassLoader的主要作用就是Java類的載入!

底層小知識:

1、在JVM類載入器中最頂層的是Bootstrap ClassLoader(引導類載入器)、Extension ClassLoader(擴充套件類載入器)、App ClassLoader(系統類載入器),App ClassLoader是預設的類載入器,如果類載入時我們不指定類載入器的情況下,預設會使用App ClassLoader載入類,ClassLoader.getSystemClassLoader()返回的系統類載入器就是AppClassLoader

然後繼續說,上面說了ClassLoader物件並不是只有一種,現在知道的有如上三種,其他的就是我們重寫的ClassLoader類了

Java自帶的三個類載入器:

Bootstrap Classloder
Extention ClassLoader
App ClassLoader

上一級稱為下一級的父載入器(怎麼理解,也就是Extention ClassLoader是App ClassLoader的父載入器),載入的先後順序依次是:Bootstrap ClassLoader => Extention ClassLoader => App ClassLoader

圖中可以知道:檢查類載入和載入類生成的時候順序是相反的,如圖中所示,當一個類載入器查詢class和resource的時候,首先判斷這個class是不是已經載入成功;如果沒有的話它並不是自己進行查詢,而是先委託給父載入器,然後遞迴委託,直到Bootstrap ClassLoader載入器;如果Bootstrap classloader找到了,直接載入並且返回class和resource;如果沒有找到,則一級一級返回,最後才是自己(這裡的自己正常的肯定就是App ClassLoader,要不然就是你的自定義的類載入器)去查詢載入。

需要知道的:某些時候我們獲取一個類的類載入器時候可能會返回一個null值,如:java.io.File.class.getClassLoader()將返回一個null物件,因為java.io.File類在JVM初始化的時候會被Bootstrap ClassLoader(引導類載入器)載入(該類載入器實現於JVM層,採用C++編寫),我們在嘗試獲取被Bootstrap ClassLoader類載入器所載入的類的ClassLoader時候都會返回null

ClassLoader類的方法

ClassLoader最重要的如下5個方法:

1. loadClass (載入指定的Java類)
2. findClass (查詢指定的Java類)
3. findLoadedClass (查詢JVM已經載入過的類)
4. defineClass (定義一個Java類)
5. resolveClass (連結指定的Java類)

第一個方法,loadClass,一開始在開頭也稍微介紹了下,該方法的作用是載入指定的Java類,跟一下這個方法

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

這裡呼叫了一個loadClass,兩個引數的方法,繼續跟

    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); //這裡先進行判斷要載入的類名對應的類例項是否已經載入過了,這裡findLoadedClass的作用其實就是 如果該類例項已經載入過了,那麼它後續的操作就會直接返回
            if (c == null) { //如果為空,那麼也就是沒有載入
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false); // 這裡判斷 父載入器是否為存在,如果存在就讓父類載入器去載入
                    } else {
                        c = findBootstrapClassOrNull(name); //如果例項化ClassLoader的時候傳入了父類載入器(new ClassLoader(你傳入的父類載入器))就使用父類載入器載入TestHelloWorld類,否則使用JVM的Bootstrap ClassLoader載入。
                    }
                } 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;
        }
    }