JVM之类文件结构(六)

    各种不同的平台虚拟机与所有平台都统一使用的程序存储格式–字节码是构成平台无关性的基石。
    除了在Java外,还有很多其它在JVM上运行的语言比如:JRuby、Scala、Groovy等。
Class文件结构:
这里写图片描述
    实现语言平台无关性的基础仍然是是虚拟机和字节码存储格式。
    Java虚拟机不与包括Java在内的任何语言绑定,它只与“Class文件”这特定二进制文件格式有关,Class文件中包含了Java虚拟机指令集和符号以及其它若干辅助信息。基于安全考虑,对Class文件做了强制性语法和结构化约束,任何一门语言都可以表示为一个被JVM所接受的有效Class文件。需要字节码命令组合实现各种语义。
    任何一个Class文件都对应着唯一一个类或接口的定义信息,但反过来说,类或接口并不一定定义在文件里(类或接口也可以通过类加载器直接生成)。
    Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑排列在Class文件中,中间没有添加任何分隔符,内容几乎全部是运行时必要的数据,没有间隙。当遇到占用超过8位字节以上空间的数据时,会按照高位在前(大端存储)的方式分割成若干个8位自己进行存储,其文件格式采用类似C语言结构体的伪结构来存储数据且只有两种数据类型:无符号数和表,后面的解析都要以这两种数据类型为基础。
    无符号数:以u1、u2、u4、u8分别代表一个字节、两个字节、四个字节、八个字节的无符号数,无符号数可以描述数字、索引引用、数量值或按照utf-8编码构成的字符串值。
    表:是由多个无符号数或者其他表作为数据项的符合数据类型,所有的表都习惯性以“_info”结尾。
这里写图片描述
    Class文件结构排布
1、魔数:每个Class文件开头四个字节为魔数,十六进制值为“oxCAFEBABE”,用于标识是一个jvm认可的Class文件,选择不使用”.class”验证是因为文件后缀是可以改的。
2、版本号:总共四个字节,第五个和第六个字节是次版本号,第七个和第八个是主版本号。Java1.7可生成主版本号最大为51.0
3、常量池:紧接着就是常量池的入口,由于常量池数据每个Class文件可能都不一样,因此长度不是固定的,需要开头一个u2类型的数据来表示常量池中常量的项数(并非字节长度,每一项常量长度可能不一样),而实际项数是数值-1,下标索引从1开始,0作为表示后面指向常量池的索引值数据在特定情况下需要表达“不引用任何一个常量池的项目”。
    常量池中每一项常量都是一个表,比如开头第一个u1(07)表示类型为:“CONSTANT_Class_info”,则表示这是一个类或接口的符号引用,则它的结构式tag(u1(07))、name_index(u2(是一个索引值指向常量池中一个CONSTANT_Utf8_info类型常量)).标志位u1已经解释过,则u1与紧跟后面u2组成“CONSTANT_Class_info”,因此第一项常量还包含后面两个字节长度的内容0x0002,表示指向第二个常量
这里写图片描述
这里写图片描述
由于Class文件中方法、字段等都需要引用CONSTANT_Utf8_info类型常量来描述名称,从上图中可以看出,tag标志位为1的未该类型常量,用u2类型表示后面字节长度,由于2个字节最多表示2的16次方(0-65535)因此方法或变量名称用英文字符最多不超过64KB(64K字节),紧跟着长度后面就是length个字节长度的内容。
Oracel公司为我们准备了一个专门分析Class文件字节码的工具 javap 在命令行中输入javap -verbose TestClass就可以把字节码内容。
这里写图片描述
CONSTANT_Class_info结构分两部分,首先是所有项都必须有的u1类型tag标志位,后面一个u2类型的index指向常量池中一个全限定名常量项(utf8)的索引。
4、访问标志
常量池后面是一个u2类型的访问标志,这个标志用于识别一些类或接口层次的访问信息:类还是接口,是否为public类型,是否为abstract类型,如果是类的话是否被声明为final等。
ACC_PUBLIC:0x0001
ACC_FINAL:0x0010
ACC_SUPER:0x0020
…..
所有符合包含的项相加值为该访问标志的值。
5、类索引、父类索引和接口索引集合
类索引和父类索引都是一个u2类型的数据,它的值是指向常量池中一个CONSTANT_Class_infol类型的常量项索引;但是由于接口可以继承多个所以是不固定的,因此分两部分:第一部分u2类型的(interfaces_count)接口计数器,用来记录接口的数量;第二部分就是interfaces_count个接口索引,指向常量池中全限定名常量项。
6、字段表集合
字段表(field_info)用于描述接口或者类中声明的变量。字段(field)包括类级变量(static修饰)和实例变量(非static修饰),但不包括方法中的变量(方法中变量存储在栈帧中的局部变量表中,一个栈帧对应一个方法执行)。
包含两部分:计数容量和项内容
包含的信息有;字段的作用域(public protect…)是实例变量还是类变量、可变性(final)、并发可变性(volatile)…..
上述各个字段都是boolean值,最终合并起来用一个值access_flags(field_info结构如下),具体的数据还是引用常量来表示,最终的数据项格式:
u2 access_flags 1 与类中的访问标志符非常相似
u2 name_index 1 字段的简单名称 inc()方法,为inc 去掉类型和参数的方法或字段名称
u2 descriptor_index 1 字段和方法的描述符 描述字段的数据类型、方法的参数列表(数量、类型及顺序)和返回值。描述符比如“java.lang.String[][]’”二维数据:”[[Ljava/lang/String;”(注意java对象用L表示,long用J表示,区别与其他的类型标识),int[]:[I; 描述方法先参数列表顺序,后返回值的顺序。参数列表要在小括号内:void inc():()V、java.lang.String.toString():()Ljava/lang/String; 、int index(char[] source,int sourceoffset,char[] target):([CI[C)I
u2 attributes_count 1
attribute_info attributes attribute_count
7、方法表集合
与字段表集合表示方法类似,内容有区别。也有一个最终的项结构类似。方法除了用户编写的外,编译器也会自动添加比如类构造器“”方法和实例构造器“”方法。
重载:需要一个与原方法相同名称,但不同特征签名,而返回值不包含在签名中,因此不能仅仅靠返回值来实现重载,因此要参数列表不同。
8、属性表集合
Class文件、字段表、方法表都可以携带自己的属性表集合。java虚拟机对这个要求就稍微宽松一些。
其中属性表中最重要的一个属性是Code,方法表中的属性表中的code属性存储方法体。
它的结构:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_26564827/article/details/80297233