【jdk源码阅读】第四篇:Launcher源码阅读(类加载器相关)

前言

宏观角度来看,JVM中实例化一个对象的基本过程如下:

类加载器主要负责将class从本地磁盘加载到内存中,ClassLoader有一种层次关系,但不是传统意义上的继承。层次关系如下:

双亲委派机制

BootstrapClassLoader用于加载lib目录下的核心库,比如String就由此加载。

ExtClassLoader加载一些拓展类,至于哪些是拓展类,文章的后面会做验证。

AppClassLoader加载classpath下的类。

为啥需要这么多加载器,而且不同的加载器有着不同的管辖范围?这就引出一个概念:”双亲委派机制“

当JVM加载一个类的时候,不会直接使用AppClassLoader,而是交给ExtClassLoader加载,ExtClassLoader加载前也会尝试用BootstrapClassLoader加载。

总的来说,就是保持一个原则:保证高层次的类加载器优先加载。

这是一种典型的特权行为,通过优先级的划分,来使得核心类库优先被加载。

这样核心类库无法被应用程序篡改,保证了JVM环境的安全。

Launcher

main()方法是Java程序的入口,而Launcher是JRE中用于启动main()方法的类,其内部定义了ExtClassLoader与AppClassLoader,负责初始化工作。

ExtClassLoader

继承体系同AppClassLoader,放在下面说了。

主要的创建逻辑在 createExtClassLoader()

获取ExtClassLoader负责的加载的class文件目录

可以打印一下

public class ClassLoaderDemo {

    public static void main(String[] args) throws ClassNotFoundException {

        String bootstrapUrl = System.getProperty("java.ext.dirs");
        String[] split = bootstrapUrl.split(";");
        for (String s : split) {
            System.out.println(s);
        }
    }
}

结果

可以看出是jre下的 ext包

AppClassLoader

继承体系

继承自URLClassLoader。通常情况下,没有一重继承,就会多一层含义、多一个作用。

SecureClassLoader

大概意思就是,AppClassLoader是根据路径来加载类资源文件的。

/jdk/bin             不以斜杠结尾,就表示加载一个名为bin的jar包

/jdk/bin/            以斜杠结尾,则表示加载这个文件夹里所有的资源。

 

回到AppClassloader类本身,我们在Laucher的构造方法中看到,获取AppClassLoader实力时,会给他传一个ExtClassLoader

仔细看这个构造方法,将var2(也就是ExtClassLoader)传给了父类的构造

父类又继续向上传

直到传到了ClassLoader

保存了下来,也就是说AppClassLoader中有一个变量parent,其值为ExtClassLoader。

 

下面到了最关键的load环节:

在ClassLoader方法内有一个很重要的私有方法,loadClassInternal

虚拟机会调用私有方法loadClassInternal,而loadClassInternal唯一做的事就是调用loadClass方法。

在经过一些类的合法性校验后,调用父类的loadClass加载类文件。ClassLoader类中的loadClass便是”双亲委派机制“的实现。

我们在自定义ClassLoader时候,必须继承ClassLoader类。其次,官方不建议直接重写loadClass方法,为了遵循双亲委派机制,我们应该保留loadClass中的代码,重写findClass方法。

当loadClass发现BootStrapClassLoader和ExtClassLoader无法加载类时,会调用findClass方法。

BootstrapClassLoader

BootstrapClassLoader显得比较神秘一点,它由C++实现,是JVM自身的一部分,我们看不到其具体的实现。

但依然可以通过一些方法 来捕捉它

String的ClassLoader理论上应该是BootstrapClassLoader

但是打印出来却是null。

我们结合ExtClassLoader的构造函数分析

第二个参数传递的是他的 父 ClassLoader(在AppClassLoader小标题下已经分析了,不重复),也传了一个null。

结合起来看,结论就出来了。

BootstrapClassLoader抽象层级较高,并且由C++实现,无法拓展,如果尝试获取某个类的ClassLoader返回值是null,则表示由BootstrapClassLoader加载。

进一步验证

我定义一个UserInfo类用于测试,尝试嵌套调用它的ClassLoader

public class ClassLoaderDemo {

    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println(UserInfo.class.getClassLoader());
        System.out.println(UserInfo.class.getClassLoader().getParent());
        System.out.println(UserInfo.class.getClassLoader().getParent().getParent());
    }
}

结果中体现的层次关系就很明显了

 

其实Launcher中有一个变量   sun.boot.class.path 存放的就是BootStrapClassLoader管辖的范围

public class ClassLoaderDemo {

    public static void main(String[] args) throws ClassNotFoundException {
        String property = System.getProperty("sun.boot.class.path");
        for (String s : property.split(";")) {
            System.out.println(s);
        }
    }
}

控制台输出如下

可见主要是jre   lib目录下的一些jar包

其他

由于篇幅原因,自定义ClassLoader、字节码加密等知识总结就不准备放一篇文章里了。

 

如有错误,欢迎批评指正!

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值