深入理解Java虚拟机笔记(三)类文件结构

无关性基石

无关性有平台无关性和语言无关性。

  1. 统一的程序存储格式----字节码
  2. 特定的二进制文件格式----“Class文件”

虚拟机不关注Class的来源是什么语言

在这里插入图片描述

Class类文件结构的基础

class文件时一组以8字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符。

当需要用到占用8字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储。

Class文件格式采用一种类似于C语言结构体系的伪结构来存储数据,这种伪结构只有两种类型:

  • 无符号数

在这里插入图片描述

魔数和Class文件的版本

每个Class文件的头4个字节称为魔数,它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。

很多文件存储标准中都使用魔数来进行身份识别,因为后缀名容易改动。

魔数的值为:OxCAFEBABE(与咖啡有关,似乎也预示着Java的商标)

紧接着魔数的4个字节是Class文件的版本号:

5~6字节是次版本号(Minor Version)

7~8字节是主版本号(Major Version)

Java版本号从45开始,高版本可以兼容低版本Class文件,但无法向上兼容

在这里插入图片描述

常量池入口

紧接着版本后是常量池入口,它是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时还是在Class文件中第一个出现的表类型数据项目。

常量池入口需要放置一项u2类型的数据,代表常量池容量计数值,不过该容量计数是从1而不是从0开始的。

常量池中主要存放两大类常量:

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

访问标志

常量池结束后紧接着的两个字节代表访问标志,该标志用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等。

在这里插入图片描述

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

Class文件用这三项数据来确定一个类的继承关系。

  • 类索引用于确定这个类的全限定名
  • 父类索引用于确定这个类的父类的全限定名,由于Java不允许多继承,所以父类索引只有一个,除了Object外,所有Java父类索引都不为0
  • 索引集合用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句后的接口顺序从左到右排列在接口索引集合中。入口为计数器

在这里插入图片描述

字段表集合

字段表(field_info)用于描述接口或者类中声明的遍历。

字段包含的信息:

  • 字段的作用域(public private protected)
  • 是实例变量还是类变量(static 修饰符)
  • 可变性(final)
  • 并发可见性(volatile修饰符,是否强制从主内存读写)
  • 是否可被序列化(transient 修饰符)
  • 字段数据类型(基本类型 对象 数组)
  • 字段名称

上述信息中,各个修饰符都是布尔值,适合用符号位表示,而字段叫什么名字,字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。

在这里插入图片描述
在这里插入图片描述
实际情况中,public,private,protected三个标志最多只能选择其中一个,final,volatile不能同时选择。接口中的字段必须要有public static final标志,这些都是由Java本身的语言规则所决定的。

name_index和descriptor_index都是对常量池的引用,分别代表着字段的简单名称以及字段和方法的描述符

  • 全限定名

    “org/fenixsoft/clazz/TestClass”是这个类的全限定名,仅仅是把类全名中的“.”替换成了“/”而已,为了使连续的多个全限定名之间不产生混淆,在使用时最后一般会加入一个“;”表示全限定名结束。

  • 简单名称

    简单名称是指没有类型和参数修饰的方法或者字段名称,这个类中的inc()方法和m字段的简单名称分别是“inc”和“m”。

  • 描述符

    描述符的作用是用来描述字段的数据类型,方法的参数列表(包括数量,类型和顺序)和返回值。

在这里插入图片描述

对于数组类型,每一维度将使用一个前置的“[”字符来描述,如一个定义为“java.lang.String[][]”类型的二维数组,将被记录为:“[[Ljava/lang/String;”,一个整型数组“int[]”将被记录为“[I”。

用描述符描述方法时,先参数列表后返回值的顺序描述,参数列表按照参数的严格顺序放在一个()之内。

字段表的固定数据项目到decriptor_index就结束了,后面跟随着的属性表存放一些额外的信息。

字段表集合中不会列出从超类或者父接口中继承而来的字段,但有可能列出原本Java代码之中不存在的字段,**譬如在内部类中为了保持对外部类的访问性,会自动添加指向外部类实例的字段。**另外,在Java语言中字段是无法重载的,两个字段的数据类型、修饰符不管是否相同,都必须使用不一样的名称,但是对于字节码来讲,如果两个字段的描述符不一致,那字段重名就是合法的。

方法表集合

方法表结构:

  • 访问标志
  • 名称索引
  • 描述符索引
  • 属性表集合

在这里插入图片描述
在这里插入图片描述
少了volatile,transient关键字,多了synchronized,native,strictfp,abstract关键字可以修饰方法

方法里面的Java代码,经过编译器编译成字节码指令后,存放在方法属性表集合中一个名为"Code"的属性里面,属性表是Class文件格式中最具拓展性的一种数据项目。

属性表集合

在Class文件,字段表,方法表都可以携带自己的属性表集合,以用于描述某些场景专有的消息

属性表集合的限制宽松了一些,不再要求各个属性表具有严格顺序,并且只要不与已有属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性消息,Java虚拟机运行时会忽略掉它不认识的属性。
在这里插入图片描述

code属性

Java程序方法体中的代码经过Javac编译器处理后,最终变为字节码指令存储在Code属性内。Code属性出现在方法表的属性集合之中,但并非所有的方法表都必须存在这个属性,譬如接口或者抽象类在的方法就不存在code属性。(即有具体方法才有code属性)

在这里插入图片描述

  • attribute_name_index:指向CONSTANT_Utf8_info 型常量的所有,常量值固定为"Code",它代表了该属性的属性名称
  • attribute_length:指示了属性值的长度
  • max_stack:代表了操作数栈深度的最大值。
  • max_locals:代表了局部变量表所需的存储空间。
  • code_length:字节码长度
  • code:用于存储字节码指令的一系列字节流。

code属性是Class文件中最重要的一共属性,如果把一共Java程序中的信息分为代码(java代码)和元数据(包括类,字段方法定义以及其他信息)两部分,那么其他数据项目描述元数据,code描述代码

初步猜测:Java虚拟机执行字节码是基于栈的体系结构

this关键字访问此方法所属的对象的实现是将其作为一个参数访问,因此每个实例方法至少都有一个隐藏参数即该方法所属的对象。

在这里插入图片描述
字节码指令之后的是该方法的显示异常处理表集合,异常表对于Code属性来说不是必然存在的。
在这里插入图片描述
在这里插入图片描述

异常执行路径:

  1. 如果try语句块中出现属于Exception或其子类的异常,则转到catch语句块处理。
  2. 如果try语句块中出现不属于Exception或其子类的异常,则转到finally语句块处理。
  3. 如果catch语句块中出现任何异常,则转到finally语句块处理。

字节码指令简介

Java虚拟机的指令由一个字节长度的,代表某种操作意义的数字(操作码)和跟随其后的0到多个操作所需参数(操作数)组成。

大多数指令只有一个操作码

在这里插入图片描述

字节码与数据类型

大多数指令都包含了其操作对于的数据类型信息,但由于指令数量只能占一个字节,所以部分操作码需要共用。

加载和存储指令

加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输。

如果某个操作结果没有明确的数学定义的话,将会使用NaN值来表示。所有使用NaN值作为操作数的算术操作,结果都会返回NaN。

指令类型

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

公有设计与私有实现

虚拟机实现方式:

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

猜你喜欢

转载自blog.csdn.net/weixin_43958969/article/details/95313605