JVM基础之类加载

674 阅读3分钟

简介

本篇文章介绍类加载相关的内容

配套视频讲解:www.bilibili.com/video/BV173… (更新可能会迟到但不会缺席)

类加载是什么

Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最 终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制 《深入理解Java虚拟机》

类加载阶段

加载

”加载“是类加载的第一个步骤主要进行以下几个操作

  • 读取class文件到内存(就是读入一个流)

  • 解析这个文件,并生成对应c++里的instaceKlass对象(instanceKlass存放在方法区)

  • 生成instanceMirrorKlass存到堆

备注: 加载环节没有规定从哪里加载class文件,所以可以是从jar,war,数据库,动态生成。

验证

总而言之就是验证当前文件是否符合java虚拟机规范以及是否能被当前版本的jvm加载

image.png

准备

为静态变量赋零值

其实就是给被static 修饰的变量赋零值

零值: 不是代码中给变量赋的真实值,可以理解为初始值。

各个类型的零值是什么就不赘述了。

public class Test {

  //这个阶段,不会给i赋值为10 而是0(int的零值)
  public static int i = 10;

  //不会给a赋零值,因为它是实例变量
  public int a = 2;
  
}

解析

解析阶段是将符号引用解析成直接引用

符号引用,直接引用是什么呢 ?

我们现在都知道,java文件被jvm加载前要经过javac编译成class字节码

image.png

我们是不是可以认为class文件包含了所有java文件的代码信息了呢。(当然是),认可了这点后我们继续

准备一个类

public class Test {
    HH h = new HH();
}

编译好后查看class文件(16进制)

image.png

看这一段

63 6f6d 2f68 616f 7a69 2f48 483b 0100 0a53 6f75 7263 6546 696c 6501 0007 4848

对照ascii转换成字符串刚好就是 com haozi HH,也就是说在class文件中 HH类还是用字符串表示的这个字符串我们可以看成是HH类的符号,这就是符号引用。直接引用就是将这个字符串换成HH类真正存放的内存地址

image.png

初始化

  • 执行clinit

clinit是编译器产生的,内容是合并,所有的类变量,和static中的语句

细节

  • clinit是线程安全的,jvm执行时会加锁。

  • 看个题目

public class Parent {

    static int i = 1;

    static {
        System.out.println("parent");
    }
}
 */
public class Son extends Parent {

    static {
        System.out.println("Son");
    }
}
public class Test {
    public static void main(String[] args) {
      int i =  Son.i;
    }
}

执行结果为什么?


结果为 控制台打印 parent,因为访问的是son.i 而i是parent的类变量,所以就要执行parent的clinit方法初始化,进而执行static代码块。