JVM(一):类加载过程

124 阅读4分钟

代码文件执行总体流程

  1. 编译:将java源文件编译成class字节码文件
  2. 类加载:将class字节码文件装载进JVM
  3. 解释:JVM将class字节码文件解释成操作系统可以识别的指令
  4. 执行:操作系统执行指令

类加载过程

  1. 装载:查找并加载类的二进制信息,在堆中生成一个java.lang.Class对象,类的基本信息存放在方法区中
  2. 验证:验证类是否符合java规范和JVM规范
  3. 准备:为静态变量赋初始值,即默认值
  4. 解析:将符号引用转化为直接引用,可以无歧义地定位直接定位到虚拟机中的目标
  5. 初始化:为静态变量赋上正确的初始值,按照静态变量、静态代码块、静态方法、构造方法的顺序由上至下执行

1. 编译

作用:将java源码文件编译成class文件

操作内容:对源代码程序做语法分析、语义分析、注解处理(Lombok)、泛型擦除等

过程

  • 词法分析:一个个字节地读取源代码,识别语言关键字,得到Token流
  • 语法分析:检查Token流中关键字组合是否符合Java语法规范,形成抽象语法树(结构化的语法表达形式)
  • 语义分析:将复杂语法转化为简单语法,如注解处理、泛型擦除、注释等,得到注解抽象语法树
  • 字节码生成:根据注解抽象语法树生成符合Java虚拟机规范的字节码

2. 加载

作用:将class文件加载到JVM中

步骤

  1. 装载

    • 时机:有需要时才进行装载(new或反射),节省内存开销

    • 操作:通过类加载器装载,防止内存中出现多份同样的字节码,使用双亲委派机制

      不会自己尝试加载这个类,而是把请求委托给父加载器,依次向上

    • 规则:本地方法由根加载器(BootstrapLoader)装载,内部实现的扩展类由扩展加载器(ExtClassLoader)装载,程序中的类由系统加载器(AppClassLoader)装载

    • 内存角度:查找并加载类的二进制数据,在堆中创建一个java.lang.Class对象,类相关信息存储在方法区中

  2. 连接:对Class对象信息进行验证、为类变量分配内存空间并赋默认值

    • 步骤:

      1. 验证:验证类是否符合Java规范和JVM规范

      2. 准备:为静态变量分配内存,初始化为默认值

      3. 解析:将符号引用转为直接引用

        符号引用:可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可,与虚拟机布局无关

        直接引用:与虚拟机布局相关,已经被加载到内存中,避免不同虚拟机对于相同符号引用翻译出的直接引用不同

  3. 初始化

    • 为静态变量赋上正确的初始值
    • 收集类中的静态变量、静态代码块、静态方法、构造方法,从上往下开始执行

3. 解释

作用:将class字节码文件转换为操作系统识别的指令

字节码解释器

  • 非热点代码:直接解释
  • 热点代码:编译,交给即时编译器
  • 热点探测:方法调用计数器、回边计数器。达到阈值后,触发JIT编译

即时编译器JIT

  • 保存热点代码的指令码,下次执行就无需重复解释,直接执行缓存的机器语言

4. 执行

操作系统执行指令码

5. 方法执行顺序

  1. 父类静态代码块
  2. 子类静态代码块
  3. 父类代码块
  4. 父类空参构造器
  5. 子类代码块
  6. 子类调用构造器

总结:静态 > 继承关系 > 代码块 > 构造器

默认子类构造器都会调用父类空参构造,如果父类无空参,子类必须说明使用的构造器

代码块早于构造器:提取各个构造器中公用部分,减少冗余