深入理解JVM——第四章 JVM执行子程序学习笔记

一、class文件结构

计算机只认识0和1,这个称之为本地机器NativeCode

Jvm的无关性

与平台无关性是建立在操作系统上,虚拟机厂商提供了许多可以运行在各种不同平台的虚拟机,它们都可以载入和执行字节码,从而实现程序的“一次编写,到处运行” 。

各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(ByteCode)是构成平台无关性的基石,也是语言无关性的基础。Java虚拟机不和包括Java在内的任何语言绑定,它只与“Class文件”这种特定的二进制文件格式所关联,Class文件中包含了Java虚拟机指令集和符号表以及若干其他辅助信息。

ClassFile {

    u4             magic;        //识别Class文件格式,具体值为0xCAFEBABE; 即魔术
    u2             minor_version;//次版本号
    u2             major_version;//主版本号
    u2             constant_pool_count;//常量池容量计数
    cp_info        constant_pool[constant_pool_count-1];

    //它代表各种各样的字符串常量、类和接口名、字段名以及在ClassFile结构及其子结构中引用的其他常量。

    //每个常量池表条目的格式由它的第一个“标记”字节表示。
    u2             access_flags;     //访问标志
    u2             this_class;       //类索引
    u2             super_class;     //父类索引
    u2             interfaces_count;//接口索引数
    u2             interfaces[interfaces_count]; //接口索引集合

    //接口数组中的每个值必须是常量池中有效索引。
    u2             fields_count;     //字段数    
    field_info     fields[fields_count];
    u2             methods_count;   //方法数
    method_info    methods[methods_count];
    u2             attributes_count;  //属性数

    attribute_info attributes[attributes_count];
}

Class类文件(了解即可)

任何一个Class文件都对应着唯一一个类或接口的定义信息,但反过来说,Class文件实际上它并不一定以磁盘文件的形式存在。Class文件是一组以8位字节为基础单位的二进制流。

Class文件格式

各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎全部是程序运行的必要数据,没有空隙存在。Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构中只有两种数据类型:无符号数和表。

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

Class文件格式详解

Class的结构不像XML等描述语言,由于它没有任何分隔符号,所以在其中的数据项,无论是顺序还是数量,都是被严格限定的,哪个字节代表什么含义,长度是多少,先后顺序如何,都不允许改变。按顺序包括:

1、魔数

每个Class文件的头4个字节称为魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。使用魔数而不是扩展名来进行识别主要是基于安全方面的考虑,因为文件扩展名可以随意地改动。文件格式的制定者可以自由地选择魔数值,只要这个魔数值还没有被广泛采用过同时又不会引起混淆即可。

2、版本号

紧接着魔数的4个字节存储的是Class文件的版本号:第5和第6个字节是次版本号Minor Version,第7和第8个字节是主版本号Major Version;譬如某个 Class 文件的主版本号为 M,副版本号为 m,那么这个Class文件的格式版本号就确定为M.m,Class 文件格式版本号大小的顺序为:1.5 < 2.0 < 2.1。

假设一个 Class 文件的格式版本号为V,仅当Mi.0 ≤ v ≤ Mj.m成立时,这个Class文件才可以被此 Java虚拟机支持。不同版本的Java虚拟机实现支持的版本号也不同,高版本号的Java虚拟机实现可以支持低版本号的Class文件,反之则不成立 。

注意:Oracle 的 JDK 在 1.0.2 版本时,支持的 Class 格式版本号范围是 45.0 至 45.3;JDK 版本在 1.1.x时,支持的 Class 格式版本号范围扩展至 45.0 至 45.65535;JDK 版本为 1. k 时(k ≥2)时,对应的 Class文件格式版本号的范围是 45.0 至 44+k.0。下表列举了Class文件版本号:

备注:十六进制34对应十进制52就是JDK1.8版本。

3、常量池

常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值(constant_pool_count)。与Java中语言习惯不一样的是,这个容量计数是从1而不是0开始的,常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。

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

常量池中的每一项都具备相同的格式特征:第一个字节作为类型标记用于识别该项是哪种类型的常量,称为“tagbyte”。常量池的索引范围是 1 至 constant_pool_count−1。
每一种类型的格式特征:这里用CONSTANT_Class_info举个例子:

4、访问标致

访问标志,access_flags 是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等

  • ACC_SYNTHETIC标志的类,意味着它是由编译器自己产生的而不是由程序员编写的源代码生成的。
  • ACC_ENUM标志的类,意味着它或它的父类被声明为枚举类型。
  • ACC_INTERFACE标志的类,意味着它是接口而不是类,反之是类而不是接口。如果一个Class文件被设置了ACC_INTERFACE标志,那么同时也得设置ACC_ABSTRACT标志(JLS §9.1.1.1)。同时它不能再设置 ACC_FINAL、ACC_SUPER 和 ACC_ENUM 标志。
  • 注解类型必定带有ACC_ANNOTATION标记,如果设置了ANNOTATION标记,ACC_INTERFACE也必须被同时设置。如果没有同时设置ACC_INTERFACE标记,那么这个Class文件可以具有表4.1中的除ACC_ANNOTATION外的所有其它标记。当然ACC_FINAL和ACC_ABSTRACT这类互斥的标记除外(JLS §8.1.1.2)。
  • ACC_SUPER标志用于确定该Class文件里面的invokespecial指令使用的是哪一种执行语义。目前Java 虚拟机的编译器都应当设置这个标志。ACC_SUPER标记是为了向后兼容旧编译器编译的Class文件而存在的,在JDK1.0.2版本以前的编译器产生的Class文件中,access_flag里面没有 ACC_SUPER 标志。同时,JDK1.0.2前的Java虚拟机遇到ACC_SUPER标记会自动忽略它。

在表 4.1 中没有使用的access_flags标志位是为未来扩充而预留的,这些预留的标志为在编译器中会被设置为0,Java虚拟机实现也会自动忽略它们。

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

 

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

类索引用(this_class)于确定这个类的全限定名,父类索引(super_class)用于确定这个类的父类的全限定名。由于Java语言不允许多重继承,所以父类索引只有一个; 但是接口可以实现好几个,所以是个集合,

除了java.lang.Object之外, 所有的Java类都有父类,因此除了java.lang.Object外,所有Java类的父类索引都不为0。

接口索引集合就用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句(如果这个类本身是一个接口,则应当是extends语句)后的接口顺序从左到右排列在接口索引集合中。

5、字段表集合

 

 

字段表,fields[]数组中的每个成员都必须是一个 fields_info 结构(§4.5)的数据项,用于表示当前类或接口中某个字段的完整描述。fields[]数组描述当前类或接口声明的所有字段,但不包括从父类或父接口继承的部分。字段(field)包括类级变量以及实例级变量。

而字段叫什么名字、字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。字段表集合中不会列出从超类或者父接口中继承而来的字段,但有可能列出原本Java代码之中不存在的字段,譬如在内部类中为了保持对外部类的访问性,会自动添加指向外部类实例的字段。

每个字段的结构都如下:

field_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

5.1、访问标识符( access_flags )如下:

对访问标识符的解释

ACC_PUBLIC、ACC_PRIVATE、ACC_PROTECTED三个标志最多只能选择其一。

接口中的字段必须有ACC_PUBLIC、ACC_STATIC、ACC_FINAL标

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值