深入理解Java虚拟机之虚拟机执行子系统 (1)

1、类文件结构

1-1、 Class类文件的结构

  • class文件是一组以字节为基础的二进制流,各个数据项严格按照顺序紧凑子排列在文件之中,中间没有添加任何分隔符。
  • Class文件格式采用一个类似C语言结构体的伪结构来存储数据,伪结构只有两种数据类型:无符号数和表
    • 无符号数属于基本的数据类型,u1、u2、u4、u8、代表一个字节,二个字节,8个字节的的无符号数。无符号数,可以用来描述数字,索引引用,数量值或按照UTF-8编码构成的字符串值
    • 表是由多个无符号数或者其他表作为数据项构成的复合数据类型所有表的命名习惯以"_info" 结尾。表用于描述有层次关系的复合结构的数据,整个Class 文件本质上可以视作为一张表。

1-1-1、魔数与Class文件的 版本

  • 每个Class 文件的头4个字节被称为魔数。唯一作用:确定这个文件是否为一个能被虚拟机接受的的class文件。(0xCAFEBABE)
  • 紧接着魔数的四个字节存储的是Class文件的版本号:第五第六字节是次版本号,第七第八为主版本号。(次版本号在JDK1.2之后未使用过,全部为零,在JDK1重新启用了次版本号,用于标识技术预览版的功能特性 标记为65535,便于Java虚拟机加载类文件时能够区分出来)。

1-1-2、常量池

  • 紧接着主次版本号之后是常量池入口,是Class文件中出现的第一个出现的表类型项目,也是Class文件中与其他项目关联最多的数据。
  • 入口放置一项u2类型的数据,代表常量池容量计数器(只有常量池的容量计算是以1开始的,不是0)
  • 常量池中主要存放 两大类常量:字面量(接近java的常量,如文本字符串,final常量)和符号引用(编译原理的方面 主要包含如下:)
    • 被 模块导出或者开放的包
    • 类和接口的全限定名
    • 字段的名称和描述符
    • 方法的名称和描述符
    • 方法句柄和方法类型
    • 动态调用点和动态常量
  • Java进行Javac编译的时候,虚拟机加载Class文件的时候进行动态连接虚拟机做类加载时,将会从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中
  • 常量池中每一项常量都是一个表,常量表中分别有17种不同类型的常量。(17类的共同特点,表结构起始的第一位是一个u1类型的标志位) (转)常量池的17中项目类型
  • 使用javap -verbose参数输出的字节码内容:其中Constant pool 代表常量池。

1-1-3、访问标志

  • 常量池结束之后,紧接着2个字节代表访问标志(用来标识一个类或接口层次的访问信息)
    在这里插入图片描述

1-1-4、类索引、父类索引与接口索引集合

  • 类索引父类索引是一个u2型的数据,接口索引集合一组u2类型的数据的集合,按照顺序排列在访问标志之后。
  • Class文件的这三项数据确定这个类型的继承关系
  • 类索引决定这个类的全限定名,父类索引决定这个类的父类索引。
  • 接口索引集合,入口的第一项u2类型的数据为接口计数器,表示索引表的容量。

1-1-5、字段表集合

  • 随后是字段表集合,字段表用来描述接口或类中声明的变量,包含类级变量以及实例级变量,但不包含在方法内部声明的局部变量。
  • 字段表集合不会列出从父类或者父接口中继承的字段,但有可能原本java代码中不存在的字段,譬如内部类保持对外部类的访问性,编译器会自动添加指向外部类的实例的字段。
  • 全限定名:把类全名中的“.”替换成“/”
  • 简单名称(name_index):指没有类型和参数修饰的方法或字段名称
  • 描述符(descriptor_index):用来描述字段的数据类型、方法的参数列表(包含数量,类型以及顺序),和返回值
    在这里插入图片描述 (access_flags)
    在这里插入图片描述

1-1-6、方法表集合

  • 方法表集合与字段表集合结构相同
  • 方法里的代码经过Javac编译成字节码指令之后,存放在方法属性表集合中一个名为“Code”的属性里。
  • 如果父类方法在子类中没有被重写,方法表集合中就不会出现来自父类的方法信息,但同样,有可能会出现由编译器添加的类构造器实例构造方法。
  • Java语言中,要重载一个方法,除了要与原方法具有相同的简单名称之外,还要求必须拥有一个和原方法不同的特征签名,Java语言无法仅仅依靠返回值的不同来对一个方法重载的Class中特征签名范围明显大一些,只要描述符不一致的两个方法就可以共存

1-1-7、属性表集合(attribute_info)

  • Class文件、字段表、方法表都可以携带自己的属性表集合,以描述某些场景专有的信息。
  • Code属性
    • 出现在方法表的属性集合中,Java程序方法体里的代码经过javac编译后,最终变为字节码存储在Code中
    • java语言的编译器应当选择异常表而不是通过跳转指令来实现Java异常和finally处理机制,
  • Exceptions属性
    • 方法描述时在throws关键字后面列举出的异常
  • LineNumberTable属性:
    • java源码行号与字节码行号(字节码的偏移量)之间的对应关系。
  • LocalVariableTable及LocalVariableTypeTable属性:
    • LocalVariableTable描述栈帧中局部变量表的变量与Java源码中定义的变量之间的关系。
    • LocalVariableTypeTable 使用字段的特征签名完成泛型的描述。
  • SourceFile及SourceDebugExtension属性:
    • SourceFile记录生成这个Class的文件的源码文件名称
  • ConstantValue属性:
    • 通知虚拟机自动为静态变量赋值。
  • InnerClass属性
    • 记录内部类与宿主类之间的关联
  • Deprecated及Syntheic属性
    • Deprecated弃用标注
    • Syntheic不是有Java源码产生的
  • StackMapTable属性
    • 会在虚拟机类加载的字节码验证阶段被新类型检查验证器使用
  • ** Signature属性**
    • 可选的定长属性,可以出现于类、字段表、方法表结构的属性表中
  • BootstrapMethods属性
    • 一个复杂的变长属性位于类文件的属性表中,这个属性用于保存invokedynamix指令引用的引导方法限定符
  • MethodParameters属性
    • 位于方法表的变长属性,记录方法的各个形参名称和信息。
  • Class模块化相关属性
    • Module
    • ModulePackages
    • ModuleMainClass
  • 运行时注释相关属性
    • Runtime…开头的六个属性

在这里插入图片描述在这里插入图片描述

1-2、字节码指令

  • 大部分与数据类型相关的字节码,操作码都有特殊的字符来表示对哪种数据类型服务:
    • ** i(int型),l(long型),s(short型),b(byte型),c(char型),f(float型),d(double型),a(reference型) **
  • 操作码长度只有一个字节
  • 加载和存储指令
  • 运算指令 ( *指令 (省略 * ) 例如 add ladd fadd dadd)
  • **类型转换指令 **
    • 宽化类型转化 (小范围到大范围转换)
    • 窄化类型转换 (大范围到小范围转换),可能发生上限溢出,下限溢出,精度丢死等,数量类型窄化转换指令永远不可能导致虚拟机抛出运行时异常。
  • 对象创建与访问指令
  • 操作数栈管理指令
  • 控制转移指令
  • 方法调用和返回指令
  • 异常处理指令
  • 同步指令

(转)其他博客的总结指令
(转)虚拟机字节码指令表

1-3、公有设计,私有实现

虚拟机的实现方式主要有两种

  • 将输入的Java虚拟机代码在加载时或执行时翻译成另一种虚拟机的指令集。
  • 将输入的Java虚拟机代码在加载时或执行时翻译成宿主机处理程序的本地指令集(即时编译器代码生成技术)

参考资料《深入理解Java虚拟机》第三版

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值