我的JVM(三):Class文件加载过程和初始化

一、概述

    前文《我的JVM(二):class文件结构》中已经详细介绍class文件的内容结构,本文主要详细讲解class文件的加载过程,并且详细分析类加载器的分类以及底层原理。

二、分析

    1. 加载过程

    class文件加载的过程分为大的三个步骤,具体如下图所示:

    此图也正好解释了对象初始化与半初始化,如上图,在Preparation阶段,静态变量被赋予了默认值,而要在Initializing阶段才被赋予初始值。

        (1) Loading:把class采用双亲委派机制load到内存中,也就是把class文件中的字节装载到内存上。

        (2) Linking:

            Linking过程中又分为以下三步:

            a. Verification:检验文件类型,class的魔数是CAFEBABE,如果是其他的格式就会被剔除掉,不进入下一阶段。

            b. Preparation:为静态变量赋默认值。

            c. Resolution:将类、方法、属性等符号引用解析为直接引用;将class文件中常量池中的符号引用解析为指针、偏移量等内存地址的直接引用。

        (3) Initializing:静态变量赋初始值,也是在这个阶段调用静态代码块。

    2. 类加载器

    类加载器可以分为4种,不同的类加载器负责加载不同的class,详情如下图所示:

    (1) BootstrapClassLoader:它是最顶层的类加载器,主要负责加载核心类。它本身是jvm内部用C++代码实现的一个模块,在java中没有一个类与之对应,所以当一个类(如String)获取它的classloader时返回值为null。也就是说只要一个类的classloader为null,就代表这个类是由BootstrapClassLoader加载的。

    (2) ExtensionClassLoader:负责加载jre/lib/ext目录下的拓展包。

    (3) AppClassLoader:负责加载classpath目录下的文件,也就是我们自己写的代码编译的文件都在这里。

    (4) CustomClassLoader:自定义类加载器,可以加载指定文件。

    类加载器在类的加载过程中采用的是双亲委派机制,流程图如下:

    当一个class文件需要被load到内存时,JVM是按需动态加载采用双亲委派机制(有一个从子到父的过程,又有一个从父到子的过程),大致过程分为8个小步骤,具体如下:

    (1) 如果有自定义的类加载器CustomClassLoader,就先尝试在CustomClassLoader的缓存中找是否被CustomClassLoader加载过了,如果加载过了则返回结果。

    (2) 如果CustomClassLoader缓存中没有,则在AppClassLoader的缓存中找是否已经被AppClassLoader加载,如果加载过了则返回结果。

    (3) 如果AppClassLoader缓存中没有,则在ExtensionClassLoader的缓存中找是否已经被ExtensionClassLoader加载,如果加载过了则返回结果。

    (4) 如果ExtensionClassLoader缓存中没有,则在BootstrapClassLoader的缓存中找是否已经被BootstrapClassLoader加载,如果加载过了则返回结果。

    (5) 如果BootstrapClassLoader缓存中也没有,那么由BootstrapClassLoader委派ExtensionClassLoader进行加载。如果ExtensionClassLoader加载成功,则返回结果。

    (6) 如果ExtensionClassLoader没有加载成功,则委派AppClassLoader进行加载。如果AppClassLoader加载成功,则返回结果。

    (7) 如果AppClassLoader没有加载成功,则委派CustomClassLoader进行加载。如果CustomClassLoader加载成功,则返回结果。

    (8) 如果到CustomClassLoader还是没有加载成功,则抛出ClassNotFoundException异常。

    那么为什么采用双亲委派的机制进行类加载呢?

    (1) 安全性(主要):class的实际加载方向是自顶向下的,所以当我们自己也写了一个String类想要去替换java类库中的String类是不可以的,因为BootstrapClassLoader会先加载java类库中的String.class,那么CustomClassLoader将我们自己写的String进行加载时,会因为BootstrapClassLoader已经加载了而不会被重复加载。

    (2) 节约资源:因为每个类加载都有自己的缓存,从而使得类不会被重复加载,节约时间提高效率。

    值得注意的是,类加载描述中的父加载器并不是指类结构上的继承关系,只是除了BootstrapClassLoader外,其他的类加载中都有一个名为parent的ClassLoader对象,也就是说这里描述的父加载器只是语义上的,不代表类加载器的加载器,也不代表类加载的父类。

    我们写些代码帮助思考,如下:

    输出结果如下:

    3. 各个类加载的范围

    有上图输出结果我们可以看到,AppClassLoader类与ExtensionClassLoader类其实都是Launcher类的内部类。Launcher类就是所有类加载的一个包装类,也称为启动类。各个类加载的范围如下图:

    4. 自定义类加载器

    自定义类加载器分为以下两步:

    (1) 继承ClassLoader:将自定义的类加载器类继承自ClassLoader类。

    (2) 重写模板方法findClass:重写findClass()方法,在没有被父加载器找到时,调用defineClass()方法根据二进制码生成对象。

    我们还可以对自定义类加载器加载自加密的class:对二进制每个字节和seed进行异或操作就完成了加密操作,别人拿到class文件也没法进行反编译得到源码,我们自己读取时再与seed异或就完成了解密操作。这样做的好处是防止反编译和防止篡改。

    5. 编译执行、解释执行与混合执行

    java默认是混合执行,检测热点代码的命令是 -XX:CompileThreshold=10000 。用于设置热点代码检测的阈值,达到阈值则对此块代码编译成为本地代码。

三、总结

    本文主要介绍了Class文件加载过程,并且在此过程中详细分析了4种类加载器以及双亲委派加载机制。

    更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java觉浅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值