JVM源码解析 从Launcher类浅谈ClassLoader(类加载器及双亲委派)

  created  by  鱼鱼 {{tag}}
创建于 2020年08月11日 00:12:44 最后修改于 2020年11月28日 23:20:25

JVM的基础——ClassLoader分类

    首先普及ClassLoader的基础:所有的Java类都是由ClassLoader由class文件加载进内存的,对于一个类,其唯一标识就是类名+加载他的ClassLoader(亦即对于不同的 ClassLoader,即使是加载了同一个Class也不能互通,本质上是两个类),其基本的分类如下图:

  • BootstrapClassLoader是一个特殊的ClassLoader,负责启动时加载jre的类库。并不继承于ClassLoader,因为是jvm逻辑的一部分;

  • ExtClassLoader也会加载jre类库,但是会加载那些额外的扩展类库(jre\lib\ext目录),到这个级别的 类加载器已经可以直接在代码中使用了;

  • AppClassLoader是加载用户编写代码的 类加载器,会加载所有classpath路径下的类,所以这也是默认的系统 类加载器,默认通过调用ClassLoader.getSystemClassLoader()可以获取;

  • 用户也可以自定义其它ClassLoader,继承于ClassLoader下的自定义 类加载器

我们通过查看源码可以看到继承于ClassLoader的加载器(除去我们看不到的BootstrapClassLoader)在 Launcher中的实现。

ExtClassLoader

    ExtClassLoader是 Launcher中的静态内部类,继承于 URLClassLoader,这个父类会通过指定的url路径去加载class,在这里展示一部分源码:

static class ExtClassLoader extends   URLClassLoader {
    private static volatile   Launcher.ExtClassLoader instance;
    //  Launcher通过这个方法获取ExtLoader实例,线程安全
    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();
        }
    }
    
    public ExtClassLoader(File[] var1) throws IOException {
        super(getExt  URLs(var1), (ClassLoader)null,   Launcher.factory);
        SharedSecrets.getJavaNetAccess().get  URLClassPath(this).initLookupCache(this);
    }
    //获取ClassLoader的文件目录集合
    //var0最后的值为 xxx\jre\lib\ext; 等
    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;
    }

    //……………………
   
}

AppClassLoader

    同ExtClassLoader也会内部静态类:

static class AppClassLoader extends   URLClassLoader {
    final   URLClassPath ucp = SharedSecrets.getJavaNetAccess().get  URLClassPath(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.pathTo  URLs(var2);
                return new   Launcher.AppClassLoader(var1x, var0);
            }
        });
    }
    
    
    AppClassLoader(  URL[] var1, ClassLoader var2) {
            //将var2作为parent
        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);
        }
    }

}

双亲委派加载模型及其实现

    在一个 类加载器创建的时候,有时会为其指定一个父加载器(并非class上的继承关系),在Java中有双亲委派的加载模型,顾名思义,就是在加载某个类的时候会优先从其父加载器中加载,依次递归。这种做法能够保证每次加载对于一个class(包括package和ClassName的完整路径)都是相同的,同时也能保证相同的class不会被覆盖,保护了jre的一些源码(举个例子,你不能直接覆盖掉java.lang.Integer这个类,因为它是由BootstrapClassLoader加载的,AppClassLoader无论如何都不能覆盖加载),相关代码在 Launcher中实现,我们以ExtLoader和AppLoader为例:

创建ClassLoader:    

public   Launcher() {
      Launcher.ExtClassLoader var1;
    try {
        //创建ExtClassLoader
        var1 =   Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }
     
    try {
        //创建了AppClassLoader,将ExtClassLoader作为参数传入
        //上面的super(var1, var2,   Launcher.factory); 一行
        this.loader =   Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }

其中创建ClassLoader的super语句首行直接将传入的ClassLoader作为了parent:

private ClassLoader(Void unused, ClassLoader parent) {
   this.parent = parent;
   //…………
}

在loadClass方法中:

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 {
                //有父加载器先从父加载器中加载class
                if (parent != null) {
                    c = parent.loadClass(name, false);
                //没有父加载器,则从bootStrap加载器中加载class
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                //都没有加载到,才从本加载器中进行class的加载
                // 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;
    }
}


评论区
评论
{{comment.creator}}
{{comment.createTime}} {{comment.index}}楼
评论

JVM源码解析 从Launcher类浅谈ClassLoader(类加载器及双亲委派)

JVM源码解析 从Launcher类浅谈ClassLoader(类加载器及双亲委派)

JVM的基础——ClassLoader分类

    首先普及ClassLoader的基础:所有的Java类都是由ClassLoader由class文件加载进内存的,对于一个类,其唯一标识就是类名+加载他的ClassLoader(亦即对于不同的 ClassLoader,即使是加载了同一个Class也不能互通,本质上是两个类),其基本的分类如下图:

我们通过查看源码可以看到继承于ClassLoader的加载器(除去我们看不到的BootstrapClassLoader)在 Launcher中的实现。

ExtClassLoader

    ExtClassLoader是 Launcher中的静态内部类,继承于 URLClassLoader,这个父类会通过指定的url路径去加载class,在这里展示一部分源码:

static class ExtClassLoader extends   URLClassLoader {
    private static volatile   Launcher.ExtClassLoader instance;
    //  Launcher通过这个方法获取ExtLoader实例,线程安全
    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();
        }
    }
    
    public ExtClassLoader(File[] var1) throws IOException {
        super(getExt  URLs(var1), (ClassLoader)null,   Launcher.factory);
        SharedSecrets.getJavaNetAccess().get  URLClassPath(this).initLookupCache(this);
    }
    //获取ClassLoader的文件目录集合
    //var0最后的值为 xxx\jre\lib\ext; 等
    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;
    }

    //……………………
   
}

AppClassLoader

    同ExtClassLoader也会内部静态类:

static class AppClassLoader extends   URLClassLoader {
    final   URLClassPath ucp = SharedSecrets.getJavaNetAccess().get  URLClassPath(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.pathTo  URLs(var2);
                return new   Launcher.AppClassLoader(var1x, var0);
            }
        });
    }
    
    
    AppClassLoader(  URL[] var1, ClassLoader var2) {
            //将var2作为parent
        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);
        }
    }

}

双亲委派加载模型及其实现

    在一个 类加载器创建的时候,有时会为其指定一个父加载器(并非class上的继承关系),在Java中有双亲委派的加载模型,顾名思义,就是在加载某个类的时候会优先从其父加载器中加载,依次递归。这种做法能够保证每次加载对于一个class(包括package和ClassName的完整路径)都是相同的,同时也能保证相同的class不会被覆盖,保护了jre的一些源码(举个例子,你不能直接覆盖掉java.lang.Integer这个类,因为它是由BootstrapClassLoader加载的,AppClassLoader无论如何都不能覆盖加载),相关代码在 Launcher中实现,我们以ExtLoader和AppLoader为例:

创建ClassLoader:    

public   Launcher() {
      Launcher.ExtClassLoader var1;
    try {
        //创建ExtClassLoader
        var1 =   Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        throw new InternalError("Could not create extension class loader", var10);
    }
     
    try {
        //创建了AppClassLoader,将ExtClassLoader作为参数传入
        //上面的super(var1, var2,   Launcher.factory); 一行
        this.loader =   Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        throw new InternalError("Could not create application class loader", var9);
    }

其中创建ClassLoader的super语句首行直接将传入的ClassLoader作为了parent:

private ClassLoader(Void unused, ClassLoader parent) {
   this.parent = parent;
   //…………
}

在loadClass方法中:

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 {
                //有父加载器先从父加载器中加载class
                if (parent != null) {
                    c = parent.loadClass(name, false);
                //没有父加载器,则从bootStrap加载器中加载class
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                //都没有加载到,才从本加载器中进行class的加载
                // 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;
    }
}



JVM源码解析 从Launcher类浅谈ClassLoader(类加载器及双亲委派)2020-11-28鱼鱼

{{commentTitle}}

评论   ctrl+Enter 发送评论