Java虛擬機(jī)--類加載器源碼

類加載器源碼分析

下面,我們就來深入的學(xué)習(xí)下類加載器的源碼,看看到底做了哪些事情?

類加載體系

上圖呈現(xiàn)是源碼級別的類加載體系,ClassLoader是基類,所有的類加載器都需要繼承它(啟動類加載器除外)。

首先,我們通過上文中的測試類來舉例,一點點剖析類加載的流程。

創(chuàng)建一個包下普通的類:com.jiaboyan.test.ObjectTest

public class ObjectTest {
}

將此類編譯,并打包處理:

jar cvf ObjectTest.jar com\jiaboyan\test\ObjectTest.class

將此jar包放入<Java_Runtime_Home>/lib/ext目錄下;

編寫測試用例:

public class JVMTest5 {
   public static void main(String[] agrs) throws ClassNotFoundException {
       Class clazz = Class.forName("com.jiaboyan.test.ObjectTest");
       System.out.println(clazz.getClassLoader());
   }
}

使用Class.forName來加載com.jiaboyan.test.ObjectTest類,看內(nèi)部具體流程。

類加載流程

(1)進(jìn)入到Class內(nèi)部,可以看到實際調(diào)用了native修飾的forName0()方法。

public static Class<?> forName(String className) throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

private static native Class<?> forName0(String name, boolean initialize,
                                        ClassLoader loader,
                                        Class<?> caller) throws ClassNotFoundException;

(2)通過debug來看,在forName0()后又調(diào)用了AppClassLoader類的loadClass(String var1, boolean var2)方法。

public Class loadClass(String var1, boolean var2) throws ClassNotFoundException {
    int var3 = var1.lastIndexOf(46);
    if (var3 != -1) {
        SecurityManager var4 = System.getSecurityManager();
        if (var4 != null) {
            var4.checkPackageAccess(var1.substring(0, var3));
        }
    }
    return super.loadClass(var1, var2);
}

在方法的末尾,調(diào)用父類中的loadClass(String name, boolean resolve)方法,也就是ClassLoader類;

(3)通過debug來看,在forName0()后又調(diào)用了ClassLoader類的loadClass(String name, boolean resolve)方法。

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
//先從緩存查找該class對象,找到就不用重新加載
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) {
//拋出異常無需理會
}

            if (c == null) {
                long t1 = System.nanoTime();
                //如果都沒有找到,則通過findClass去查找并加載
                c = findClass(name);
                .....
            }
        }
        //是否需要在加載時進(jìn)行解析
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

loadClass源碼所示:當(dāng)類加載請求到來時,先從緩存中查找該類對象,如果存在則直接返回,如果不存在則交給該類加載器的父類加載器去加載,倘若沒有父類加載器則交給頂級啟動類加載器去加載,最后仍沒有找到,則使用findClass()方法去加載。

實際流程是,類加載請求進(jìn)來時,this為AppClassLoader對象,判斷AppClassLoader對象的parent父類加載器是否為空。根據(jù)雙親委派模型得知,此時的parent一定為ExtClassLoader對象。調(diào)用ExtClassLoader的loadClass(String name, boolean resolve)方法。

通過源碼得知,ExtClassLoader繼承ClassLoader抽象類,并且沒有重寫loadClass(String name, boolean resolve)方法。那么,此時又進(jìn)入到了上面的loadClass(String name, boolean resolve)中。不過,此時的this是ExtClassLoader對象。

ExtClassLoader對象的parent父類加載器為null,調(diào)用findBootstrapClassOrNull(String name)方法,使用頂層啟動類加載器去加載com.jiaboyan.test.ObjectTest類。

由于頂層啟動類加載器是C++實現(xiàn),我們無法直接獲取到其引用,最終調(diào)用到了native修飾的findBootstrapClass(String name)方法。

由于,我們將ObjectTest.jar放在了<Java_Runtime_Home>/lib/ext目錄下,所以頂層啟動類加載器加載不到com.jiaboyan.test.ObjectTest類,繼而拋出異常,返回到ExtClassLoader對象的loadClass(String name, boolean resolve)方法中來。

(4)接下來,繼續(xù)調(diào)用findClass(name)方法。

protected Class<?> findClass(final String name) throws ClassNotFoundException{
    try {
        return AccessController.doPrivileged(
            new PrivilegedExceptionAction<Class>() {
                public Class run() throws ClassNotFoundException {
                    String path = name.replace('.', '/').concat(".class");
                    Resource res = ucp.getResource(path, false);
                    if (res != null) {
                        try {
                            //生成class對象
                            return defineClass(name, res);
                        } catch (IOException e) {
                            throw new ClassNotFoundException(name, e);
                        }
                    } else {
                        throw new ClassNotFoundException(name);
                    }
                }
            }, acc);
    } catch (java.security.PrivilegedActionException pae) {
        throw (ClassNotFoundException) pae.getException();
    }
}

由于現(xiàn)在的this為ExtClassLoader對象,所以我們調(diào)用ExtClassLoader對象的findClass(final String name)方法。通過查看源碼發(fā)現(xiàn),ExtClassLoader并沒有findClass方法,不過再其父類URLClassLoader中有實現(xiàn)(代碼如上)。

(5)調(diào)用defineClass(String name, Resource res)方法。

private Class defineClass(String name, Resource res) throws IOException {
    long t0 = System.nanoTime();
    int i = name.lastIndexOf('.');
    URL url = res.getCodeSourceURL();
    if (i != -1) {
        String pkgname = name.substring(0, i);
        Manifest man = res.getManifest();
        if (getAndVerifyPackage(pkgname, man, url) == null) {
            try {
                if (man != null) {
                    definePackage(pkgname, man, url);
                } else {
                    definePackage(pkgname, null, null, null, null, null, null, null);
                }
            } catch (IllegalArgumentException iae) {
                if (getAndVerifyPackage(pkgname, man, url) == null) {
                    throw new AssertionError("Cannot find package " + pkgname);
                }
            }
        }
    }
    java.nio.ByteBuffer bb = res.getByteBuffer();
    if (bb != null) {
        //獲取資源內(nèi)的字節(jié)流數(shù)組:
        CodeSigner[] signers = res.getCodeSigners();
        CodeSource cs = new CodeSource(url, signers);
        sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
        return defineClass(name, bb, cs);
    } else {
        //獲取資源內(nèi)的字節(jié)流數(shù)組:
        byte[] b = res.getBytes();
        CodeSigner[] signers = res.getCodeSigners();
        CodeSource cs = new CodeSource(url, signers);
        sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
        return defineClass(name, b, 0, b.length, cs);
    }
}

defineClass(String name, Resource res)方法是用來將byte字節(jié)流解析成JVM能夠識別的Class對象,通過這個方法不僅能夠通過class文件實例化class對象,也可以通過其他方式實例化class對象,如通過網(wǎng)絡(luò)接收一個類的字節(jié)碼,然后轉(zhuǎn)換為byte字節(jié)流創(chuàng)建對應(yīng)的Class對象。

最后調(diào)用了defineClass(String name, java.nio.ByteBuffer b,CodeSource cs)方法,具體內(nèi)部細(xì)節(jié)筆者不在詳細(xì)陳序,需要說明的是:Class對象依舊使用了native修飾的方法來完成,內(nèi)部細(xì)節(jié)無從得知。

private native Class defineClass2(String name, java.nio.ByteBuffer b,
                                  int off, int len, ProtectionDomain pd,
                                  String source);

(6)至此ExtClassLoader對象的加載階段就此結(jié)束,Class對象返回。當(dāng)返回到AppClassLoader對象中時,c并不為null,所以無需再次調(diào)用c = findClass(name)方法,整個類加載完成。

通過上述源碼可知,當(dāng)我們自己定義一個類加載器時候,無需重寫loadClass()方法,直接重寫自定義的findClass(String name)即可。父類加載器加載失敗時候,最終都會走到findClass(String name)中來。

說完了類加載主體流程,接下來我們來研究一點細(xì)節(jié)的東西!?。?!

此時,將文章拉回上面源碼體系截圖中,我們來看看SecureClassLoader、URLClassLoader類起到了哪些作用。

SercureClassLoader

SercureClassLoader繼承ClassLoader,擴(kuò)展了ClassLoader類的功能,增加對代碼源和權(quán)限定義類的驗證。

URLClassLoader

URLClassLoader繼承SercureClassLoader,實現(xiàn)了ClassLoader中定義的方法,例如:findClass()、findResource()等。

在URLClassLoader中有一個成員變量ucp--URLClassPath對象,URLClassPath的功能是通過傳入的路徑信息獲取要加載的字節(jié)碼,字節(jié)碼可以是在.class文件中、可以是在.jar包中,也可以是在網(wǎng)絡(luò)中。

public URLClassLoader(URL[] urls) {
    super();
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkCreateClassLoader();
    }
    ucp = new URLClassPath(urls);
    this.acc = AccessController.getContext();
}

在URLClassPath構(gòu)造中,需要傳入URL[]數(shù)組,通過這個URL[]數(shù)組中所指定的位置信息,去加載對應(yīng)的文件。在URLClassPath內(nèi)部會根據(jù)傳遞的路徑是文件地址、jar包地址還是網(wǎng)絡(luò)地址來進(jìn)行判斷,來生成對應(yīng)Loader。

public URLClassPath(URL[] var1, URLStreamHandlerFactory var2) {
    this.path = new ArrayList();
    this.urls = new Stack();
    this.loaders = new ArrayList();
    this.lmap = new HashMap();
    this.closed = false;

    for(int var3 = 0; var3 < var1.length; ++var3) {
        this.path.add(var1[var3]);
    }

    this.push(var1);
    if (var2 != null) {
        this.jarHandler = var2.createURLStreamHandler("jar");
    }

}

根據(jù)路徑的不同,來生成不同的Loader:

private URLClassPath.Loader getLoader(final URL var1) throws IOException {
    try {
        return (URLClassPath.Loader)AccessController.doPrivileged(new PrivilegedExceptionAction<URLClassPath.Loader>() {
            public URLClassPath.Loader run() throws IOException {
                String var1x = var1.getFile();
                if (var1x != null && var1x.endsWith("/")) {
                    return (URLClassPath.Loader)("file".equals(var1.getProtocol()) ? new URLClassPath.FileLoader(var1) : new URLClassPath.Loader(var1));
                } else {
                    return new URLClassPath.JarLoader(var1, URLClassPath.this.jarHandler, URLClassPath.this.lmap);
                }
            }
        });
    } catch (PrivilegedActionException var3) {
        throw (IOException)var3.getException();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容