jdk-Launcher源码学习

源码类位置:sun.misc.Launcher、java.lang.ClassLoader

注意:建议阅读前先了解一下双亲委派机制:jvm的类加载器(classloader)及类的加载过程

背景

        sun.misc.Launcher是类加载器实始化过程中会创建的一个实例,也是说整个JVM启动只会创建仅有一个Launcer的实例,所以该对角是一个核心。而Lancer中使用了单例模式进行创造,通过创建完实例后再进入初始化类加载器双亲委派的实例。

为什么要有双亲委派机制?直接加载不是效率更高?

1.双亲委派机制也叫沙箱安全机制,主要是防止核 心API被随意篡改。

2.避免类重复加载,每次加载先寻找父类是否加载过,如果加载了加载类全局仅加载过1次;

分析如下

以下是Launcher的构建方法代码分析

//构建方法
public Launcher() {
    Launcher.ExtClassLoader var1;
    try {
        //获取扩展类加载器
        var1 = Launcher.ExtClassLoader.getExtClassLoader();
    } catch (IOException var10) {
        //失败抛出异常提示
        throw new InternalError("Could not create extension class loader", var10);
    }

    try {
        //初始化appClassLoader同时通过appClassLoader进行加载 扩展类加载器类对象(就是我们自定义类,自已编写程序)
        this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
    } catch (IOException var9) {
        //失败抛出异常提示
        throw new InternalError("Could not create application class loader", var9);
    }
    //设置当前线程为这个this.loader(就是classLoader)
    Thread.currentThread().setContextClassLoader(this.loader);
    //获取系统配置
    String var2 = System.getProperty("java.security.manager");
    //不为空 并且不等于""和default 进行单例加载(这里用到了单例模式)
    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);
        }
        //进行设置安全管理器 就是校验是不是java.lang下面的对象
        System.setSecurityManager(var3);
    }

}

Launcer下面还有一个AppClassLoader内部类值得一读,位置:sun.misc.Launcher.AppClassLoader#loadClass

//可以发现AppClassLoader是继承URLClassLoader而不是extClassLoader 所以这里的关系不是继续只是引用
static class AppClassLoader extends URLClassLoader {
    //获取当前类URL地址
    final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
    //通过类信息获取appClassLoader的方法
    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的方法)
    AppClassLoader(URL[] var1, ClassLoader var2) {
        //调用父类获取
        super(var1, var2, Launcher.factory);
        //初始化jdk信息并放到缓存中
        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);
        }
    }
    //获取指定代码的权限
    protected PermissionCollection getPermissions(CodeSource var1) {
        PermissionCollection var2 = super.getPermissions(var1);
        var2.add(new RuntimePermission("exitVM"));
        return var2;
    }
    //判断是否已上锁 (有可能其它对象正在加载)
    private void appendToClassPathForInstrumentation(String var1) {
        assert Thread.holdsLock(this);

        super.addURL(Launcher.getFileURL(new File(var1)));
    }
    //获取上下文信息
    private static AccessControlContext getContext(File[] var0) throws MalformedURLException {
        PathPermissions var1 = new PathPermissions(var0);
        ProtectionDomain var2 = new ProtectionDomain(new CodeSource(var1.getCodeBase(), (Certificate[])null), var1);
        AccessControlContext var3 = new AccessControlContext(new ProtectionDomain[]{var2});
        return var3;
    }
    //初始化类加载器
    static {
        ClassLoader.registerAsParallelCapable();
    }
}

可以发现上面的双亲委派是先在AppClassLoader进行加载,如果加载不成功,则由父类加载,而父类是URLClassLoader 中再去调用extClassLoader,所以很多面试的时候直接上来就问是不是继承,这里不是继承后,从源码也可以看出来。(坑来的)

位置:java.lang.ClassLoader#loadClass(java.lang.String, boolean)

//类加载方法
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) {
                    //通过父类加载
                    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();
                //当前类进行加载(也就是说,如果父类或者顶级父类都没办法加载就自已加载)
                //注意这里的加载是通过URLClassLoader加载的
                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();
            }
        }
        //如果传进来是true 通过链接指定类(一般不会执行,都是传false)
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

最后

    通过学习launcher的核心代码可以发现,我们常说的双亲委派实现方式其实不是直接继承,而是引用或者说调用,其次,通过学习可以发现其实代码量不多(指java)很多本地方式其实是c代码或c++,其实通过学习是如何初始化和双亲委派的实现逻辑后续去学习如何打破双亲委派可以更深刻理解,当然本文没有读整个文件,有部分可能描述有误,其实有发现同学可以指出或交流。

b1e53b175e95f58bac5d7275086d511a.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
jdk-11-windows是指Java开发工具包(Java Development Kit)的第11个版本,适用于Windows操作系统。JDK是使用Java语言进行开发的应用程序和软件的环境,它提供了许多工具和功能,可用于编写、编译和运行Java程序。 JDK-11包含了许多新的特性和改进,使得Java编程更加方便和高效。其中一项突出的特性是JVM(Java虚拟机)的性能改进,它可以提供更快速和更高效的执行速度。其他一些特性包括G1垃圾回收器的增强和适用于观察、诊断和监控Java程序的Java Flight Recorder和Java Mission Control工具等。 对于Windows用户来说,JDK-11提供了易于安装和使用的Windows平台上的开发环境。它包括了JRE(Java运行环境)和用于开发、调试和测试Java应用程序的工具集。通过JDK-11,开发人员可以利用Windows操作系统的优势和特性,进行快速、高效和可靠的Java开发。 此外,JDK-11还提供了许多与安全性和稳定性相关的改进。它包含了新的安全性特性,可以帮助开发人员保护他们的应用程序免受潜在的安全威胁。另外,JDK-11还包含了一些稳定性改进,可以提高应用程序的性能和可靠性。 总之,JDK-11-windows是适用于Windows操作系统的Java开发工具包的最新版本。它提供了许多新的特性和改进,使得Java开发更加方便、高效和安全。对于Windows用户来说,使用JDK-11可以提供更好的开发体验,使他们能够更轻松地创建出优秀的Java应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值