天天看點

JVM類加載機制簡述及Launcher源碼分析

類加載運作全過程

當我們使用Java指令運作某個類時,會先通過類加載器加載類檔案到jvm。大緻流程如下:

① 建立引導類加載器

② C++建立JVM啟動器,執行個體化一個Launcher對象(單例)

③ Launcher對象效用getLauncher方法建立其他的類加載器,先執行個體化擷取一個ExtClassLoader(擴充類加載器)、再擷取AppClassLoader。

JVM預設使用Launcher的getClassLoader()方法傳回的類加載器AppClassLoader的執行個體,調用AppClassLoader的loadClass方法加載我們的應用程式。

JVM類加載機制簡述及Launcher源碼分析

類加載流程

執行個體化的Launcher源碼如下:

首先通過C++建立JVM啟動器,并加載Launcher,在加載Launcher時,會首先初始化靜态的對象 private static Launcher launcher = new Launcher();

//初始化Launcher對象
public Launcher() {
    Launcher.ExtClassLoader var1;
    try {
        var1 = Launcher.ExtClassLoader.getExtClassLoader();//初始化擴充類加載器ExtClassLoader
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }
		//初始化應用程式加載器AppClassLoader,并設定其父加載器為ExtClassLoader
    try {
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }
		//設定目前線程的類加載器為應用程式加載器AppClassLoader
    Thread.currentThread().setContextClassLoader(this.loader);
    String var2 = System.getProperty("java.security.manager");
    if (var2 != null) {
        SecurityManager var3 = null;
        if (!"".equals(var2) && !"default".equals(var2)) {
            try {
                var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
            } catch (IllegalAccessException var5) {
            } catch (InstantiationException var6) {
            } catch (ClassNotFoundException var7) {
            } catch (ClassCastException var8) {
            }
        } else {
            var3 = new SecurityManager();
        }
        if (var3 == null) {
            throw new InternalError("Could not create SecurityManager: " + var2);
        }
        System.setSecurityManager(var3);
    }

}           
//調用AppClassLoader的loadClass方法加載我們的應用程式,最終會調用ClassLoader的loadClass
//這一步其實就是類加載器和雙親委派機制,參考本篇文章下面章節的描述
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);//找父 類加載器加載class,加載到則不為null
                    } else {
                        c = findBootstrapClassOrNull(name);//父類為null表示為引導類加載器,因為引導類加載器是C++建立的在java中就是null
                    }
                } catch (ClassNotFoundException e) {
                    
                }

                if (c == null) {
                    // 如果到引導類加載器仍沒有加載到class,則再依次反轉查找,
                   //即再由擴充類加載器ExtClassLoader加載,ExtClassLoader再加載不到就由應用程式加載器AppClassLoader加載,總會被加載到的
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);//自己寫的類最後還是通過AppClassLoader的findClass找到(其實是父類的函數),AppClassLoader的父類是URLClassLoader
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }           

類加載器loadClass方法的類加載過程

步驟:加載 >> 驗證 >> 準備 >> 解析 >> 初始化 >> 使用 >> 解除安裝

加載:在硬碟上查找并通過IO讀入位元組碼檔案,使用到類時才會加載,例如調用類的main()方法,new對象等等,在加載階段會在記憶體中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種資料的通路入口。

驗證:校驗位元組碼檔案的正确性。

準備:給類的靜态變量配置設定記憶體,并賦予預設值。

解析:将符号引用替換為直接引用,該階段會把一些靜态方法(符号引用,比如main()方法)替換為指向資料所存記憶體的指針或句柄等(直接引用),這是所謂的靜态連結過程(類加載期間完成),動态連結是在程式運作期間完成的将符号引用替換為直接引用。

初始化:對類的靜态變量初始化為指定的值,執行靜态代碼塊。

JVM類加載機制簡述及Launcher源碼分析

loadclass過程

類加載器及雙親委派機制

Java裡有如下幾種類加載器:

引導類加載器:負責加載支撐JVM運作的位于JRE的lib目錄下的核心類庫,比如rt.jar、charsets.jar等

擴充類加載器:負責加載支撐JVM運作的位于JRE的lib目錄下的ext擴充目錄中的JAR類包。

應用程式類加載器:負責加載ClassPath路徑下的類包,主要就是加載你自己寫的那些類。

自定義加載器:負責加載使用者自定義路徑下的類包。

JVM類加載機制簡述及Launcher源碼分析

類加載器雙親委派機制

雙親委派機制:Java類先由父類加載器加載,不行再由子類加載器加載。

打破雙親委派機制方式:自定義類加載器(重寫findClass方法),并重寫loadClass方法。

為什麼要設計雙親委派機制?

沙箱安全機制:自己寫的java.lang.String.class類不會被加載,這樣便可以防止核心API庫被随意篡改。

避免類的重複加載:當父親已經加載了該類時,就沒有必要子ClassLoader再加載一次,保證被加載類的唯一性。

Tomcat自定義加載器就是打破雙親委派機制,自定義類加載器。

備注

本文部分文案來自于圖靈課堂講義。

繼續閱讀