当前位置:编程学习 > JAVA >>

谈谈Java虚拟机——Class文件结构

大家都知道,Java之所以如此受人喜欢,很大的原因是要规于它的跨平台性。“一次编写,到处运行”,Java诞生之时曾提出的著名的宣传口号,充分表达了软件开发人员对冲破平台界限的渴求。

或许大部分程序员都认为Java虚拟机执行Java程序是一件理所当然和天经地义的事,但时至今日,商业机构和开源机构已经在Java语言之外发展出一大批在Java虚拟机之上运行的语言,如Clojure、Groovy、JRuby、Jython、Scale等。使用Java编译器可以把Java代码编译为存储字节码的Class文件,使用JRuby等其它语言的编译器一样可以把程序代码编译成Class文件,Java之所以能够跨平台运行,是因为Java虚拟机可以载入和执行同一种平台无关的字节码。也就是说,实现语言平台无关性的基础是虚拟机和字节码存储格式,虚拟机并不关心Class的来源是什么语言,只要它符合Class文件应有的结构就可以在Java虚拟机中运行。

\

Class类文件的结构

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

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

无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节、8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值,或者按照UTF-8编码构成字符串值。

表是由多个无符号数或者其它表作为数据项构成的复合数据类型,所有表都习惯性地以"_info"结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表。 它由下表所示的数据项构成。

\

接下来我们根据表中的数据项来描述class文件的格式。

    1. 魔数

    每个Class文件的头4个字节称为魔数(Magic Number),它的唯一作用是用于确定这个文件是否为一个能被虚拟机接受的Class文件。很多文件存储标准中都使用魔数来进行身份识别,譬如图片格式,如gif或jpeg等在文件头中都存有魔数。Class文件魔数的值为0xCAFEBABE。如果一个文件不是以0xCAFEBABE开头,那它就肯定不是Java class文件。

    2. 版本号

    紧接着魔数的4个字节存储的是Class文件的版本号:第5和第6是次版本号(Minior Version),第7个和第8个字节是主版本号(Major Version)。Java的版本号是人45开始的,JDK1.1之后的每个JDK大版本发布主版本号向上加1,高版本的JDK能向下兼容以前版本的Class文件,但不能运行以后版本的Class文件,即使文件格式并未发生变化。JDK1.1能支持版本号为45.0~45.65535的Class文件,JDK1.2则能支持45.0~46.65535的Class文件。JDK1.7可生成的Class文件主版本号的最大值为51.0。 

    \

     3.常量池

     紧接着魔数与版本号之后的是常量池入口,常量池是Class文件结构中与其它项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时它还是在文件中第一个出现的表类型数据项目。由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值(constant_pool_count)。从1开始计数。第0项腾出来满足后面某些指向常量池的索引值的数据在特定情况下需要表达"不引用任何一个常量池项目"的意思,这种情况就可以把索引值置为0来表示。但尽管constant_pool列表中没有索引值为0的入口,缺失的这一入口也被constant_pool_count计数在内。例如,当constant_pool中有14项,constant_poo_count的值为15。Class文件结构中只有常量池的容量计数是从1开始的,对于其他集合类型,包括接口索引集合、字段表集合、方法表集合等的容量计数都是从0开始的。

     常量池之中主要存放两大类常量:字面量和符号引用。字面量比较接近于Java语言层面的常量概念,如文本字符串、被声明为final的常量值等。而符号引用则属于编译原理方面的概念,包括了下面三类常量:

  •      类和接口的全限定名
  •      字段的名称和描述符
  •      方法的名称和描述符  

    Java代码在进行Java编译的时候,并不像C和C++那样有"连接"这一步骤,而是在虚拟机加载Class文件的时候进行动态连接。也就是说,在Class文件中不会保存各个方法和字段的最终内存布局信息,因此这些字段和方法的符号引用不经过转换的话是无法被虚拟机使用的。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析并翻译到具体的内存地址之中。 

    常量池中的每一项常量都是一个表,共有11种结构各不相同的表结构数据,这11种表都有一个共同的特点,就是表开始的第一位是一个u1类型的标志位(tag,取值为1至12,缺少标志为2的数据类型),代有当前对象属于哪种常量类型,11常量类型所代表的具体含义如下表所示。

    \

    说了这么多,恐怕还是对常量池有点迷惑吧,我们举个例子来看一下

    假如我们得到的Class文件的十六进制数的一段序列为:

    \

    第9位 16转换为十进制为22,代表常量池中有21个常量。第10位的07带表的是一个常量的tag值,可以从上表中看到,07代表CONSTANT_Class_info类型,从上表中可以看出,

 

 

CONSTANT_Class_info类型的结构有一个u1类型的tag,有一个u2类型的name_index,数量都是1。那么可以看出接下来的第11位与第12位的值0002就是name_index的值。即指向的常量池中的第一个常量。第二项常量的标志位为0x01(看第13位),也就是CONSTANT_Utf_info类型,

 

CONSTANT_Utf_info类型有u2型的length与u1型的bytes。依此类推。

   如上所述,虚拟机加载Class文件的时候,就是这样从常量池中得到相对应的数值。

   4.访问标志

   紧接常量池后的两个字节称为access_flags,它展示了文件中定义的类或接口的几段信息。例如,访问标志指明文件中定义的是类还是接口;访问标志还定义的在类或接口的声明中,使用了哪种修饰符oder和接口是抽象的,还是公共的;类的类型可以为final,而final类不可能是抽象的;接口不能为final类型的。这些标志位的定义如下表所示:

    \

    如一个TestClass类被public关键字修饰但没有被声明为final和abstract,并且它使用了JDK1.2之后的编译器进行编译,因此它的ACC_PUBLIC、ACC_SUPER标志应该为真。因此它的access_flags的值应为:0x0001|0x0020 = 0x0021。

    5. 类索引

    访问标志后面接下来的两个字节是类索引(this_class),它是一个对常量池的索引。在this_class位置的常量池入口必须为CONSTANT_Class_info表。该表由两个部分组成——tag和name_index。tag部分是代表其的标志位,name_index位置的常量池入口为一个包含了类或接口全限定名的CONSTANT_Utf8_info表。 

    6.父类索引

    在class文件中,紧接在this_class之后是super_class项,它是一个两个字节的常量池索引。在super_class位置的常量池入口是一个指向该类超类全限定名的CONSTANT_Class_info入口。因为Java程序中所有对象的基类都是java.lang.Object类,除了Object类以外,常量池索引super_class对于所有的类均有效。对于Object类,super_class的值为0。对于接口,在常量池入口super_class位置的项为java.lang.Object

   7.interfaces_count和interfaces

   紧接着super_class的是interfaces_count,此项的含义为:在文件中出该类直接实现或者由接口所扩展的父接口的数量。在这个计数的后面,是名为interfaces的数组,它包含了对每个由该类或者接口直接实现的父接口的常量池索引。每个父接口都使用一个常量池中的CONSTANT_Class_info入口来描述,该CONSTANT_Class_info入口指向接口的全限定名。这个数组只容纳那些直接出现在类声明的implements子句或者接口声明的extends子句中的父接口。超类按照在implements子句和extends子句中出现的顺序在这个数组中显现。 

   8. fields_count和fields

   在class文件中,紧接在interfaces后面的是对在该类或者接口中所声明的字段的描述。首先是名为fields_count的计数,它是类变量和实例变量的字段的数量总和。在这个计数后

补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,