Java 類加載器源碼解析

1. 類加載器的源碼實現

Java 類加載機制與對象實例化 已經闡明瞭 Java 中類加載器的種類,簡單來說 Java 自帶有三個類加載器,它們的具體作用就是根據需要去動態地將 class 文件加載到 JVM 虛擬機中去。這三個類加載器對應的實現如下:

  1. 啓動類加載器,Bootstrap ClassLoader
    加載系統屬性 sun.boot.class.path 指定的路徑下的類,它由C/C++編寫,是虛擬機的一部分,無法在 java 代碼中獲知其實現
  2. 擴展類加載器,Extension ClassLoader
    加載系統屬性java.ext.dirs指定的路徑下的類,實現類爲 sun.misc.Launcher.ExtClassLoader
  3. 應用程序類加載器,Application ClassLoader
    加載環境變量 ClassPath 中的類庫,也就是系統屬性java.class.path指定的路徑,實現類爲 sun.misc.Launcher.AppClassLoader

在這裏插入圖片描述

1.1 抽象的類加載器 ClassLoader

java.lang.ClassLoader 是 Java 層面對類加載器的抽象,這個類規範了類加載的基本流程,其中比較重要的屬性及方法如下:

  1. parent:父類加載器的引用,一個類加載器通常都會保存一個父類加載器的引用,用於實現雙親委派機制
  2. loadClass()方法,該方法爲類加載器的核心方法,其中實現了雙親委派的邏輯
public abstract class ClassLoader {
    // The parent class loader for delegation
    // Note: VM hardcoded the offset of this field, thus all new fields
    // must be added *after* it.
    private final ClassLoader parent;

    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;
        }
    }

1.2 擴展類加載器 ExtClassLoader

sun.misc.Launcher.ExtClassLoader 是擴展類加載器的實現類,該類比較重要的方法如下:

  1. getExtDirs()方法中指定了其加載系統屬性java.ext.dirs指定的路徑下的類
  2. 其帶參構造方法調用父類構造方法,傳入的父類加載器參數明確指定爲 null,也就是擴展類加載器沒有 Java 層面的父類加載器
static class ExtClassLoader extends URLClassLoader {
        private static volatile Launcher.ExtClassLoader instance;

        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
            if (instance == null) {
                Class var0 = Launcher.ExtClassLoader.class;
                synchronized(Launcher.ExtClassLoader.class) {
                    if (instance == null) {
                        instance = createExtClassLoader();
                    }
                }
            }

            return instance;
        }

        private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
            try {
                return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
                    public Launcher.ExtClassLoader run() throws IOException {
                        File[] var1 = Launcher.ExtClassLoader.getExtDirs();
                        int var2 = var1.length;

                        for(int var3 = 0; var3 < var2; ++var3) {
                            MetaIndex.registerDirectory(var1[var3]);
                        }

                        return new Launcher.ExtClassLoader(var1);
                    }
                });
            } catch (PrivilegedActionException var1) {
                throw (IOException)var1.getException();
            }
        }

        void addExtURL(URL var1) {
            super.addURL(var1);
        }

        public ExtClassLoader(File[] var1) throws IOException {
            super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }

        private static File[] getExtDirs() {
            String var0 = System.getProperty("java.ext.dirs");
            File[] var1;
            if (var0 != null) {
                StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
                int var3 = var2.countTokens();
                var1 = new File[var3];

                for(int var4 = 0; var4 < var3; ++var4) {
                    var1[var4] = new File(var2.nextToken());
                }
            } else {
                var1 = new File[0];
            }

            return var1;
        }
        
     ......
}

1.3 應用程序類加載器 AppClassLoader

應用程序類加載器也叫系統類加載器sun.misc.Launcher.AppClassLoader 是其實現類,該類比較重要的方法如下:

  1. getAppClassLoader() 方法指定了其加載系統屬性java.class.path指定的路徑下的類
  2. 重寫的 loadClass()方法做了加載 class 文件的一個優化,如果某個類存在於緩存目錄但是又不在內存中,則直接由其進行連接,否則還是需要交給父類加載器去加載
static class AppClassLoader extends URLClassLoader {
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
            final String var1 = System.getProperty("java.class.path");
            final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
                public Launcher.AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                    return new Launcher.AppClassLoader(var1x, var0);
                }
            });
        }

        AppClassLoader(URL[] var1, ClassLoader var2) {
            super(var1, var2, Launcher.factory);
            this.ucp.initLookupCache(this);
        }

        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));
                }
            }

            if (this.ucp.knownToNotExist(var1)) {
                Class var5 = this.findLoadedClass(var1);
                if (var5 != null) {
                    if (var2) {
                        this.resolveClass(var5);
                    }

                    return var5;
                } else {
                    throw new ClassNotFoundException(var1);
                }
            } else {
                return super.loadClass(var1, var2);
            }
        }
    ......
}

2. 雙親委託機制的實現

在這裏插入圖片描述

上圖是一個可能的類加載流程,流程中不少重要的步驟是 native 方法實現,在 Java 層無法得知細節。Java 層加載類的核心方法代碼如下,簡單描述的話主要是以下幾個步驟:

  1. 一個 AppClassLoader 加載類時,首先查看該類是否已經被加載過,有則從緩存中獲取,否則需要委託給父類加載器去處理
  2. ExtClassLoader處理流程和 AppClassLoader 完全一致,如果它也沒有加載過目標類,則由Bootstrap ClassLoader去執行加載邏輯,也就是在 sun.mic.boot.class配置的路徑去目標類加載,加載完成就返回,沒有加載到就讓子加載器自己去找
  3. Bootstrap ClassLoader如果沒有成功加載到目標類,則ExtClassLoader自己在java.ext.dirs配置的路徑中去查找目標類加載,成功就返回,不成功再向下讓子加載器去加載類
  4. ExtClassLoader加載類不成功,AppClassLoaderjava.class.path配置的路徑下查找目標類,找到就加載返回。如果沒有找到接着讓子類加載器找,如果沒有子類就會拋出各種異常
 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); // 1.首先判斷類是否已經被加載
            if (c == null) { // 2. 未被加載則進行雙親委派加載
                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) { //3.父類加載器 loadClass 未加載到類才調用 findClass
                    // 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;
        }
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章