贼详细但又贼容易理解的类加载过程

Posted offerNotFound

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贼详细但又贼容易理解的类加载过程相关的知识,希望对你有一定的参考价值。

本文视频:寒食君

什么是类加载?

众所周知,Java 代码能在各个操作系统上运行起来,靠的是 JVM。Java 文件通过javac编译成class文件,这种中间码被我们称为字节码,然后由 JVM 加载字节码。运行时解释器将字节码解释成一行一行的机器码来执行。在程序运行期间,即时编译器会针对热点代码,直接将该热点代码编译成机器码来获得更高的执行效率。

上述的介绍中可以知道这就分为两个部分,编译器我们理解不了,但 JVM 加载字节码这个过程(就是类加载)就需要我们去了解了。

类加载的过程

类加载过程就只有加载、连接、初始化这三步

这张基本是所有博客里常见的,但可以说这张图的表述有点不准确。因为解析部分是灵活的,它可以在初始化环节之后再进行,实现所谓的“后期绑定”。其他环节的顺序不可改变。

  1. 加载:加载是一个读取 Class 文件(泛指各种来源的二进制流),将其转化为某种静态数据结构存储在方法区内,并在堆中生成一个便于用户调用的 java.lang.Class 类型的对象的过程。
  2. 验证(在 JVM 不断的迭代中,验证的东西会越来越多,越来越完善):验证这一步其实是分为很多个步骤,分散在其它步骤里的。① 文件格式验证(发生在加载阶段) ② 元数据、字节码的验证(保证这玩意不会对 JVM 有威胁,发生在验证阶段) ③ 符号引用验证(发生在解析阶段)
  3. 准备:就是为该类型中定义的静态变量赋 0 值(静态变量与成员变量是两码事)。
  4. 解析(静态解析、动态解析):将符号引用替换为直接引用
    ① 当一个 Java 类被编译成 Class 之后(假设这个类为 A),并且 A 中引用了 B,那么在编译阶段,A 是不知道 B 有没有被编译的。所以 A 并不知道 B 的实际地址,此时在 A 的 class 文件中,将使用一个字符串 S 来代替 B 的地址,这个 S 就是是符号引用。
    ② 在运行时,如果 A 发生了类加载,到解析阶段就会发现 B 还未被加载,那么将会触发 B 的类加载,将 B 加载到 JVM 中,此时 A 中 B 的符号引用将会被替换为直接引用。
    =========》上面两步说的是静态解析的情况(B 是具体的实现类)。
    ③ 如果使用了多态,B 是一个抽象类或接口呢?这就涉及到动态解析了。可能这个 B 有两个具体的实现类 C 和 D,此时并不知道 B 的具体实现是调用 C 还是 D。所以就得等到运行过程中发生了调用(即初始化之后),JVM 中的栈就会得到具体的类型信息,这时候再进行解析,就能用明确的直接引用来替换符号引用了。
    =========》 这就是动态解析,用它来实现了后期绑定,底层对应了 invokevirtual 这条字节码指令。
  5. 初始化:就是如果一个变量没有给他赋初始值的话,JVM 会给其赋默认值。

最后总结的图:

以上是关于贼详细但又贼容易理解的类加载过程的主要内容,如果未能解决你的问题,请参考以下文章

贼详细但又贼容易理解的类加载过程

贼详细但又贼容易理解的类加载过程

Java中类加载器的分析与理解!详细解析类的加载过程

深入理解java虚拟机---对象的创建过程

深入理解JVM(③)虚拟机的类加载过程

Android的类加载浅析