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

猜你喜欢

转载自blog.csdn.net/weixin_45505313/article/details/106547155