美文网首页JVM
JVM中类加载的过程

JVM中类加载的过程

作者: imchenway | 来源:发表于2021-07-06 10:33 被阅读0次

    引言

    众所周知,Java的slogan就是"Write once, run anywhere.",这也就意味着无论我们在什么平台的机器上用Java去做实现,都可以在任何支持Java的系统上直接运行,无需做任何额外操作。
    Java是如何做到这些的呢?答案是JRE。

    • 那么什么是JRE?为什么叫JRE?
      • JRE(Java Runtime Environment),是一个Java代码的运行时环境,属于软件层,运行在操作系统软件之上,属于JDK的一部分。
    • 什么是JDK?
      • JDK(Java Development Kit),每一个JDK都包含了一个兼容的JRE和一个JVM,并且JDK包含了许多Java开发人员常用的工具以及类库,比如javacjavajarjmapjstatjstackjinfort.jar等。
    • 什么是JVM?
      • JVM(Java Virtual Machine),JVM可以理解为是一个运行在操作系统之上的虚拟电脑,当我们通过javac*.java编译成JVM可识别*.class字节码文件后,再执行java,此时JVM会将*.class字节码文件解释成当前操作系统平台可识别的机器码去执行。这样的话就实现了"Write once, run anywhere."。
    • 整体流程如下所示

    JVM类加载的过程

    加载阶段:

    1. 通过类的全限定名来读取class字节码文件的二进制流
    2. 将字节流中的静态数据结构转化为方法区的运行时数据结构
    3. 在内存中生成代表这个类的java.lang.Class对象,作为方法区中这个类中的各种数据结构的访问入口
    • 注意:
      • 这只是类加载的其中一个阶段,不要和类加载混淆
      • 加载阶段和链接阶段中的部分动作是交叉进行的,加载阶段尚未完成,链接阶段可能已经开始

    链接阶段(linking)

    1. 验证
      1. 文件格式验证:确保class文件的字节流中包含的信息符合虚拟机规范,并且不会危害虚拟机自身的安全
      2. 元数据验证:语义分析
        1. 是否有父类(除java.lang.Object外,所有的类都必须有父类)
        2. 是否继承了不该继承的类
        3. 如果不是抽象类,是否实现了父类或接口中要求实现的所有方法
        4. 字段、方法是否与父类冲突
      3. 字节码验证:
        1. 确定语义合法、符合逻辑
        2. 类的方法不会做出危害虚拟机的事件
      4. 符号引用验证:发生将符号引用转化为直接引用的时候 -> 解析时
        1. 全限定名是否能找到对应的类
        2. 指定类中是否存在被引用的方法和字段
        3. 符号引用中的类、字段、方法的访问性是否可以被当前类访问(private、protected、public、default)
    2. 准备:
      1. 为类变量(被static修饰的)在方法区中分配内存,实例变量在对象实例化时分配在Java堆中
      2. 设置初始值,此时是赋零值,真正的值在初始化时完成赋值;finnal例外,直接赋值;
      public static int value = 99;
      public static final int value = 99;
      准备阶段结束后
      public static int value = 0;
      public static final int value = 99;
      
    3. 解析:将常量池中的符号引用替换为直接引用
      1. 类或接口解析
      2. 字段解析
      3. 类方法解析
      4. 接口方法解析

    初始化

    1. 这是类加载过程的最后一步,除了在加载阶段用户可以通过自定义类加载器参与,其他阶段皆由虚拟机主导和控制
    2. 在该阶段开始真正初始化类中定义的Java程序代码(或者说是字节码),调用<clinit>()
      在遇到以下几种情况时,触发初始化:
      • 当虚拟机启动时,初始化用户指定的主类;
      • 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类;
      • 当遇到调用静态方法的指令时,初始化该静态方法所在的类;
      • 当遇到访问静态字段的指令时,初始化该静态字段所在的类;
      • 子类的初始化会触发父类的初始化;
      • 如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化;
      • 使用反射 API 对某个类进行反射调用时,初始化这个类;
      • 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。

    至此,一个字节码文件便已经初始化完成。

    本文总结

    加载一个class类的过程总体分三个步骤,加载、链接、初始化,其中链接阶段分为验证、准备、解析三个阶段,加载阶段通过类的全限定名来读取class字节码文件的二进制流,并将字节码数据转化为方法区的运行时数据结构。链接阶段中的验证阶段对字节码文件进行格式和安全校验,准备阶段为类中的部分变量(被static修饰的变量)分配内存和初始值的赋值,解析阶段将常量池中的符号引用替换为直接引用。初始化阶段会将静态代码的赋值操作和静态代码块中的代码交给<clinit>()方法进行初始化,完成变量的赋值已经资源的分配。

    相关问题

    虚拟机是如何做到通过类的全限定名去找到对应的Class文件的?

    什么是符号引用和直接引用?

    相关文章

      网友评论

        本文标题:JVM中类加载的过程

        本文链接:https://www.haomeiwen.com/subject/mytgultx.html