image-20221224214330355

字节码

字节码简介

  • java源码经过编译生成之后二进制称为字节码文件,不一定是,也可能是网络传过来符合字节码规范的二进制流
  • JVM通过字节码保证平台无关性
  • Java并不是唯一生成class文件的语言
  • Class是结构紧凑的二进制流,格式固定,要求严谨

字节码组成结构

  • 魔数:区分文件类型的依据,字节码文件的前四个字节为魔数(字节码文件魔数为CA FE BA BE)
  • 文件版本:

    • 5-6字节为次要版本,7-8为主要版本号 Java 8=52.0
    • 字节码版本高于JVM版本,会产生UnsupportedClassVersion
    • | JDK版本号 | Class版本号 | 16进制 |
      | --------- | ----------- | ----------- |

    | 1.1 | 45.0 | 00 00 00 2D |
    | 1.2 | 46.0 | 00 00 00 2E |
    | 1.3 | 47.0 | 00 00 00 2F |
    | 1.4 | 48.0 | 00 00 00 30 |
    | 1.5 | 49.0 | 00 00 00 31 |
    | 1.6 | 50.0 | 00 00 00 32 |
    | 1.7 | 51.0 | 00 00 00 33 |
    | 1.8 | 52.0 | 00 00 00 34 |
    | 9 | 53.0 | 00 00 00 35 |
    | 10 | 54.0 | 00 00 00 36 |
    | 11 | 55.0 | 00 00 00 37 |
    | 12 | 56.0 | 00 00 00 38 |
    | 13 | 57.0 | 00 00 00 39 |
    | 14 | 58.0 | 00 00 00 3a |
    | 15 | 59.0 | 00 00 00 3b |
    | 16 | 60.0 | 00 00 00 3c |
    | 17 | 61.0 | 00 00 00 3d |
    | 18 | 62.0 | 00 00 00 3e |

  • 常量池

    • 字面量

      • 字符串
      • final修饰
    • 符号引用

      • 类和接口的全限定名
      • 字段名称与描述符
      • 方法名称与描述符
    • 常量池类型数据表
  • 类索引

    • 标识父类以及实现了哪些接口
  • 访问标志

    • AccessFlag访问表
  • 字段表

    • 哪些字段,以及类型
  • 方法表

    • 方法名、方法参数、返回值
  • 属性表

字节码指令

字节码指令将源码编译时由编译器生成保存在Method描述中的,与平台无关,运行时,jvm读取后翻译各平台底层指令,总数不超过256个

image-20221224204058016

例子:

invokevirtual #8 // cp_info#8:java/lang/StringBuilder.append

执行StringBuilder对象的append方法

new #6 // cp_info#6:<java/langStringBuilder>

实例化新的StringBuilder对象并将实例化的对象保存至堆中

字节码指令分类

  • 加载与存储指令
  • 运算指令
  • 类型转换指令
  • 对象创建与访问指令
  • 操作数栈管理
  • 控制转移指令
  • 方法调用与返回指令
  • 同步指令指令
  • 异常处理指令

image-20221224214220215

类加载子系统

类加载执行过程

image-20221224221128982

类加载子系统是字节码文件到数据区经历的过程

类加载子系统负责从文件或者网络中加载Class字节流读取符合jvm规范的字节码信息,并在存储到方法区中

类加载过程

image-20221224221849939

加载阶段

读取字节码二进制流

解析字节码二进制流的静态数据转换为运行时JVM方法区数据

生成类的实例对象,放入堆中,作为方法区

image-20221224223617188

若运行时需要到A类,但是不存在A类时,A类会被类加载器加载,并将A的Class实例放到堆中,将A类的字段、方法描述放在方法区中,同时A被加载的时候A的父类同时被加载

Class实例被创建的时机:

  • new实例化的时候

    • A a = new A();
  • 反射

    • Class clzA = Class.forName("com.mc10010.A")
  • 子类加载时父类同时加载
  • jvm启动时,包含main方法的主类
  • 1.7的动态类型语言支持

链接阶段

  • 验证阶段

    • 确保字节码符合虚拟机规范

      • 文件格式校验

        • 是否以CAFEBABE开头
        • 版本号是否匹配
      • 元数据匹配、语义分析

        • 是否有父类
        • 是否继承final修饰的类
        • 其他有悖Java规则的错误
      • 字节码验证

        • 类型转换是否有效
        • 是否可以执行到return
      • 符号引用

        • ClassNotFoundException
        • NoSuchMethodError
      • 全部都是基于字节码对应的二进制流进行的,基于文件本身的处理
    • image-20221225124058696
  • 准备阶段

    • 为static赋予初始值,这里只赋值初始值,而不是赋值!!!

      • 例如public static int a = 100; 在这个阶段int只赋值为0
  • 解析阶段(核心所在)

    • 将字节码静态字面关联转换成JVM内存中的动态指针关联
    • 其实就是把字符串关联改成指针关联
    • image-20221225124246193

初始化

最后修改:2023 年 02 月 23 日
如果觉得我的文章对你有用,请随意赞赏