4、JVM-类文件和字节码指令

类文件结构

概述

我们所编写的每一行代码,要在机器上运行,最终都需要编译成二进制的机器码 CPU 才能识别。但是由于虚拟机的存在,屏蔽了操作系统与 CPU 指令集的差异性,类似于 Java 这种建立在虚拟机之上的编程语言通常会编译成一种中间格式的文件来存储,比如我们今天要聊的字节码(ByteCode)文件。

什么是字节码指令?

Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的操作码(opcode)以及跟随其后的零至移个代表此操作所需参数的操作数(operand)所构成。虚拟机中许多指令并不包含操作数,只有一个操作码。

比如:操作码(操作数)
在这里插入图片描述

解读方式

如何解读供虚拟机解释执行的二进制字节码?

  • 一个一个二进制的看。用的是Notepad++,需要安装一个HEX一Editor插件,或者使用Binary Viewer
  • 使用javap指令:jdk自带的反解析工具,终端输入以下指令

javap -v xxx.class 写入文件 javap -v xxx.class >xxx.txt

  • 使用IDEA插件:jclasslib或jclasslib bytecode viewer客户端工具。(可视化更好)

文件结构

Class 文件是一组以 8 位字节为基础单位的二进制流,各个数据严格按照顺序紧凑的排列在 Class 文件中,中间无任何分隔符,这使得整个 Class 文件中存储的内容几乎全部都是程序运行的必要数据,没有空隙存在。当遇到需要占用 8 位字节以上空间的数据项时,会按照高位在前的方式分割成若干个 8 位字节进行存储。

Java 虚拟机规范规定 Class 文件格式采用一种类似与 C 语言结构体的伪结构体来存储数据,这种伪结构体中只有两种数据类型:无符号数

  • 无符号数属于基本的数据类型,以 u1、u2、u4、u8来分别代表 1 个字节、2 个字节、4 个字节和 8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8 编码结构构成的字符串值。
  • 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以「_info」结尾。表用于描述有层次关系的复合结构的数据,整个 Class 文件就是一张表,它由下表中所示的数据项构成。
    在这里插入图片描述
    在这里插入图片描述

魔数

  • Magic Number (魔数)Class文件的标志
  • 每个 Class 文件开头的4个字节的无符号整数称为魔数(Magic Number)它的唯一作用是确定这个文件是否为一个能被虚拟机接受的有效合法的Class文件。即:魔数是Class文件的标识符。
  • 魔数值固定为0xCAFEBABE。不会改变。
  • 如果一个Class文件不以0xCAFEBABE开头,虚拟机在进行文件校验的时候就会直接抛出以下错误:

Error: A JNI error has occurred, please check your installation and try again Exception in thread “main” java.lang.ClassFormatError: Incompatible magic value 1885430635 in classfile StringTest

版本号

  • 紧接着魔数的4个字节存储的是Class文件的版本号。同样也是4个字节。第5个和第6个字节所代表的含义就是编译的副版本号minor_version,而第7个和第8个字节就是编译的主版本号major_version
  • 它们共同构成了class文件的格式版本号。譬如某个Class文件的主版本号为M,副版本号为m,那么这个Class文件的格式版本号就确定为M.m

常量池

主版本号之后是常量池入口,常量池可以理解为 Class 文件之中的资源仓库,它是 Class 文件结构中与其他项目关联最多的数据类型,也是占用 Class 文件空间最大的数据项目之一,同是它还是 Class 文件中第一个出现的表类型数据项目。

因为常量池中常量的数量是不固定的,所以在常量池入口需要放置一个 u2 类型的数据来表示常量池的容量constant_pool_count,和计算机科学中计数的方法不一样,这个容量是从 1 开始而不是从 0 开始计数。之所以将第 0 项常量空出来是为了满足后面某些指向常量池的索引值的数据在特定情况下需要表达不引用任何一个常量池项目的含义,这种情况可以把索引值置为 0 来表示。

Class 文件结构中只有常量池的容量计数是从 1 开始的,其它集合类型,包括接口索引集合、字段表集合、方法表集合等容量计数都是从 0 开始。

常量池中主要存放两大类常量:字面量符号引用

  • 字面量比较接近 Java 语言层面的常量概念,如字符串、声明为 final 的常量值等。
  • 符号引用属于编译原理方面的概念,包括了以下三类常量:
    • 类和接口的全限定名
    • 字段的名称和描述符
    • 方法的名称和描述符

常量类型和结构:
在这里插入图片描述
描述符:
在这里插入图片描述

访问标志

紧接着常量池之后的两个字节代表访问标志(access_flag),这个标志用于识别一些类或者接口层次的访问信息,包括这个 Class 是类还是接口;是否定义为 public 类型;是否定义为 abstract 类型;如果是类的话,是否被申明为 final 等。具体的标志位以及标志的含义见下表:
在这里插入图片描述

类索引、父类索引与接口索引集合

类索引(this_class)和父类索引(super_class)都是一个 u2 类型的数据,而接口索引集合(interfaces)是一组 u2 类型的数据集合,Class 文件中由这三项数据来确定这个类的继承关系。

  • 类索引用于确定这个类的全限定名
  • 父类索引用于确定这个类的父类的全限定名
  • 接口索引集合用于描述这个类实现了哪些接口

字段表

  • fields_count (字段计数器)

fields_count的值表示当前class文件fields表的成员个数。使用两个字节来表示。fields表中每个成员都是一个field_info结构,用于表示该类或接口所声明的所有类字段或者实例字段,不包括方法内部声明的变量,也不包括从父类或父接口继承的那些字段。

  • 字段表集合(field_info)用于描述接口或者类中声明的变量。字段(field)包括类变量和实例变量,但不包括方法内部声明的局部变量。

    字段表的结构:
    在这里插入图片描述
    解释:

    类型名称含义数量
    u2access_flags访问标志1
    u2name_index字段名索引1
    u2descriptor_index描述符索引1
    u2attrubutes_count属性计数器1
    attribute_infoattributes属性集合attributes_count

方法表集合

  • methods_count (方法计数器)

methods_count的值表示当前class文件methods表的成员个数。使用两个字节来表示。
methods表中每个成员都是一个method_info结构。

  • methods:指向常量池索引集合,它完整描述了每个方法的签名。

在字节码文件中,每一个method_info项都对应着一个类或者接口中的方法信息。比如方法的访问修饰符(publicprivateprotected),方法的近回值类型以及方法的参数信息等。如果这个方法不是抽象的或者不是native的,那么字节码中会体现出来。一方面,methods表只描述当前类或接口中声明的方法,不包括从父类或父接口继承的方法。另一方面,methods表有可能会出现由编译器自动添加的方法,最典型的便是编译器产生的方法信息(比如:类初始化方法clinit和实例初始化init)。
在这里插入图片描述

属性表集合

  • attributes_count(属性计数器)

attributes_countclass的值表示当前文件属性表的成员个数。属性表中每一项都是一个 attribute_info结构

  • attribute_info属性表

Class 文件、字段表、方法表中都可以携带自己的属性表(attribute_info)集合,用于描述某些场景专有的信息。属性表集合不像 Class文件中的其它数据项要求这么严格,不强制要求各属性表的顺序,并且只要不与已有属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟机在运行时会略掉它不认识的属性。
在这里插入图片描述

参考博客:

  • https://juejin.cn/post/6913784171391352839
  • https://thinkwon.blog.csdn.net/article/details/103835168

Javap指令解析

https://juejin.cn/post/6913784500040400904#heading-5

字节码指令解析

https://juejin.cn/post/6914896173371719687#heading-10

https://juejin.cn/post/6915224643628433421

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值