(二) launcher详解 (类加载机制 第二篇)

sun.misc.Launcher类

  • sun.misc.Launcher类是不开源的,我们只能通过IDEA反编译看到代码,根据第一篇,我们知道,类加载的神器主要是在Launcher这个类中,这个类是jvm底层,进行类加载的。

  • JVM启动入口,主要作用是:创建ExtClassLoader 、用ExtClassLoader作为parent去创建AppClassLoader、设置AppClassLoader为当前线程的ContextClassLoader。

  • Launcher 只是一个封装了虚拟机的执行外壳,它是jvm虚拟机的入口应用,由它负责装载JRE环境和windows平台下的jvm.dll动态链接库

Launcher的结构如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IeojDd24-1636008611074)(C:\Users\ZUHAO.OUYANG\AppData\Roaming\Typora\typora-user-images\image-20211103202217163.png)]

launcher构造方法和ExtClassLoader,AppClassLoader内部类

Launcher的构造方法
//该构造方法主要是为了承担  加载ExtClassLoader和AppClassLoader加载器,并且将AppClassLoader加载器设置为当前上下文的线程
public Launcher() {
    	//申明了ExtClassLoader的变量
        Launcher.ExtClassLoader var1;
        try {
            //调用方法实例化ExtClassLoader
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            //将ExtClassLoader实例为var1当成AppClassLoader的父加载器传递进去
            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);
        }

    }

ExtClassLoader类

接下来分析一下 ExtClassLoader.getExtClassLoader(); 方法
这里将 ExtClassLoader类的定义拷贝过来了,但是只保留了比较重要的方法
可以 看到 扩展类加载器其实是继承了 URLClassLoader类的 而UrlClassLoader类则继承了
SecureClassLoader类,它又继承了ClassLoader类,所以我在之前那篇 自定义类加载器 博客中讲到过,除根类加载器外,启动类加载器都直接或间接的继承了 ClassLoader类

/*
     * The class loader used for loading installed extensions.
     */
    static class ExtClassLoader extends URLClassLoader {

        static {
            ClassLoader.registerAsParallelCapable();
        }

        /**
         * create an ExtClassLoader. The ExtClassLoader is created
         * within a context that limits which files it can read
         */
        public static ExtClassLoader getExtClassLoader() throws IOException
        {
        	// 获取扩展类加载器加载哪些目录下的类文件
            final File[] dirs = getExtDirs();

            try {

                return AccessController.doPrivileged(
                    new PrivilegedExceptionAction<ExtClassLoader>() {
                        public ExtClassLoader run() throws IOException {
                            int len = dirs.length;
                            for (int i = 0; i < len; i++) {
                                MetaIndex.registerDirectory(dirs[i]);
                            }
                            // 直接创建一个扩展类加载器返回
                            return new ExtClassLoader(dirs);
                        }
                    });
            } catch (java.security.PrivilegedActionException e) {
                throw (IOException) e.getException();
            }
        }

        /*
         * 这个扩展类加载器的构造方法
         */
        public ExtClassLoader(File[] dirs) throws IOException {
            super(getExtURLs(dirs), null, factory);
            SharedSecrets.getJavaNetAccess().
                getURLClassPath(this).initLookupCache(this);
        }

        
         /*
         *   其实扩展类加载器加载的目录就保存在 java.ext.dirs这个环境变量中,
         *   如果我们想知道扩展类加载器到底加载哪些目录下的文件 ,
         *   则我们也可以调用System.getProperty("java.ext.dirs");这个方法来获取
         */
        private static File[] getExtDirs() {
            String s = System.getProperty("java.ext.dirs");
            File[] dirs;
            // 如果这个环境变量的值不是为null的话
            if (s != null) {
            	// 可以理解为将一个字符串按照指定的分隔符分开 类似于 String.split()方法
                StringTokenizer st =
                    new StringTokenizer(s, File.pathSeparator);
                int count = st.countTokens();
                dirs = new File[count];
                for (int i = 0; i < count; i++) {
                    dirs[i] = new File(st.nextToken());
                }
            } else {
                dirs = new File[0];
            }
            // 最后就是返回了一个File数组对象
            return dirs;
        }

}

AppClassLoader类

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

ClassLoader类中loadclass()方法

我们来看下应用程序类加载器AppClassLoader加载类的双亲委派机制源码,

AppClassLoader 的loadClass方法最终会调用其父类ClassLoader的loadClass方法,该方法的大体逻辑如下:

  1. 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接 返回。
  2. 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加 载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加 载。
  3. 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的 findClass方法来完成类加载。
//这个方法是实现双亲委派机制的核心代码

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先,检测是否已经加载
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                    	//父加载器不为空则调用父加载器的loadClass
                        c = parent.loadClass(name, false);
                    } else {
                    	//父加载器为空则调用Bootstrap Classloader
                        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();
                    //父加载器没有找到,则调用findclass
                    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()
                resolveClass(c);
            }
            return c;
        }
    }

总结:

Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,之前的int.class,String.class都是由它加载。然后呢,我们前面已经分析了,JVM初始化sun.misc.Launcher并创建Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器。Bootstrap没有父加载器,但是它却可以作用一个ClassLoader的父加载器。比如ExtClassLoader。这也可以解释之前通过ExtClassLoader的getParent方法获取为Null的现象

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值