1. 程式人生 > >個人筆記--Java類加載

個人筆記--Java類加載

VM def 二進制 from path leg 源碼 ssl ted

java類加載流程:

其實是通過ClassLoader(類加載器)去把對應的class文件類型動態加載到JVM中。

其中類加載器有三個,在sun.misc.Launcher這個Java虛擬機的入口應用裏,Launcher初始化ExtClassLoader和AppClassLoader。並通過System.getProperty(“sun.boot.class.path”)得到了BootStrapClassLoader。

加載順序:

BootStrapClassLoader(啟動類加載器):通過sun.boot.class.path(可修改加載路徑)加載JRE目錄下的jar包和class文件。

ExtClassLoader(擴展類加載器):通過java.ext.dirs(可修改加載路徑)加載lib\ext。

AppClassLoader(系統類加載器):加載當前java工程目錄bin下的class文件。

AppClassLoader的parent是ExtClassLoader,ExtClassLoader的parent是null。

Bootstrap ClassLoader是由C/C++編寫的,它本身是虛擬機的一部分,所以它並不是一個JAVA類,也就是無法在java代碼中獲取它的引用。

Bootstrap沒有父加載器,但是它卻可以作用一個ClassLoader的父加載器。比如ExtClassLoader。

雙親委托機制:

一個類加載器查找class和resource時,是通過“委托模式”進行的,它首先判斷這個class是不是已經加載成功,如果沒有的話它並不是自己進行查找,而是先通過父加載器,然後遞歸下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果沒有找到,則一級一級返回,最後到達自身去查找這些對象。

委托是從下向上,然後具體查找過程卻是自上至下。

技術分享圖片

重要方法:

loadClass():

執行findLoadedClass(String)去檢測這個class是不是已經加載過了。 
執行父加載器的loadClass方法。如果父加載器為null,則jvm內置的加載器去替代,也就是Bootstrap ClassLoader。這也解釋了ExtClassLoader的parent為null,但仍然說Bootstrap ClassLoader是它的父加載器。
如果向上委托父加載器沒有加載成功,則通過findClass(String)查找。 如果class在上面的步驟中找到了,參數resolve又是true的話,那麽loadClass()又會調用resolveClass(Class)這個方法來生成最終的Class對象。

雙親委托源代碼:

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{
        synchronized (getClassLoadingLock(name)) {
            // 首先,檢測是否已經加載
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        //父加載器不為空則調用父加載器的loadClass
                        c = parent.loadClass(name, false);
                    } else {
                        //父加載器為空則調用Bootstrap Classloader
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }
                if (c == null) {
                    long t1 = System.nanoTime();
                    //父加載器沒有找到,則調用findclass
                    c = findClass(name);
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                //調用resolveClass()
                resolveClass(c);
            }
            return c;
        }
}

不管是Bootstrap ClassLoader還是ExtClassLoader等,這些類加載器都只是加載指定的目錄下的jar包或者資源。如果在某種情況下,我們需要動態加載一些東西呢?比如從D盤某個文件夾加載一個class文件,或者從網絡上下載class主內容然後再進行加載,這樣可以嗎?

如果要這樣做的話,需要我們自定義一個classloader。

步驟:

編寫一個類繼承自ClassLoader抽象類。
復寫它的findClass()方法。
在findClass()方法中調用defineClass()。
註意:如果要編寫一個classLoader的子類,也就是自定義一個classloader,建議覆蓋findClass()方法,而不要直接改寫loadClass()方法。

defineClass():

這個方法在編寫自定義classloader的時候非常重要,它能將class二進制內容轉換成Class對象,如果不符合要求的會拋出各種異常。

技術分享圖片

Context ClassLoader:線程上下文類加載器

查看Thread.java源碼可以發現:

ContextClassLoader只是一個成員變量,通過setContextClassLoader()方法設置,通過getContextClassLoader()設置。

每個Thread都有一個相關聯的ClassLoader,默認是AppClassLoader。並且子線程默認使用父線程的ClassLoader除非子線程特別設置。

個人筆記--Java類加載