【深入Java虚拟机】之二:Class类文件结构

Java是与平台无关的语言,这得益于Java源代码编译后生成的存储字节码的文件,即Class文件,以及Java虚拟机的实现。不仅使用Java编译器可以把Java代码编译成存储字节码的Class文件,使用JRuby等其他语言的编译器也可以把程序代码编译成Class文件,虚拟机并不关心Class的来源是什么语言,只要它符合一定的结构,就可以在Java中运行。Java语言中的各种变量、关键字和运算符的语义最终都是由多条字节码命令组合而成的,因此字节码命令所能提供的语义描述能力肯定会比Java语言本身更强大,这便为
摘要由CSDN通过智能技术生成

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17675609

 

平台无关性

    Java是与平台无关的语言,这得益于Java源代码编译后生成的存储字节码的文件,即Class文件,以及Java虚拟机的实现。不仅使用Java编译器可以把Java代码编译成存储字节码的Class文件,使用JRuby等其他语言的编译器也可以把程序代码编译成Class文件,虚拟机并不关心Class的来源是什么语言,只要它符合一定的结构,就可以在Java中运行。Java语言中的各种变量、关键字和运算符的语义最终都是由多条字节码命令组合而成的,因此字节码命令所能提供的语义描述能力肯定会比Java语言本身更强大,这便为其他语言实现一些有别于Java的语言特性提供了基础,而且这也正是在类加载时要进行安全验证的原因。

 

类文件结构

    Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎全部都是程序运行的必要数据。根据Java虚拟机规范的规定,Class文件格式采用一种类似于C语言结构体的伪结构来存储,这种伪结构中只有两种数据类型:无符号数和表。无符号数属于基本数据类型,以u1、u2、u4、u8来分别代表1、2、4、8个字节的无符号数。表是由多个无符号数或其他表作为数据项构成的符合数据类型,所有的表都习惯性地以“_info”结尾。

    整个Class文件本质上就是一张表,它由如下所示的数据项构成。

    从表中可以看出,无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的该数据项的形式,称这一系列连续的摸一个类型的数据为某一类型的集合,比如,fields_count个field_info表数据构成了字段表集合。这里需要说明的是:Class文件中的数据项,都是严格按照上表中的顺序和数量被严格限定的,每个字节代表的含义,长度,先后顺序等都不允许改变。

    下表列出了Class文件中各个数据项的具体含义:

    从表中可以看出,无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会在其前面使用一个前置的容量计数器来记录其数量,而便跟着若干个连续的数据项,称这一系列连续的某一类型的数据为某一类型的集合,如:fields_count个field_info表数据便组成了方法表集合。这里需要注意的是:Class文件中各数据项是按照上表的顺序和数量被严格限定的,每个字节代表的含义、长度、先后顺序都不允许改变。

   magic与version

    每个Class文件的头4个字节称为魔数magic),它的唯一作用是判断该文件是否为一个能被虚拟机接受的Class文件。它的值固定为0xCAFEBABE。紧接着magic的4个字节存储的是Class文件的次版本号和主版本号,高版本的JDK能向下兼容低版本的Class文件,但不能运行更高版本的Class文件。

   constant_pool

    major_version之后是常量池constant_pool)的入口,它是Class文件中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一。

    常量池中主要存放两大类常量:字面量和符号引用。字面量比较接近于Java层面的常量概念,如文本字符串、被声明为final的常量值等。而符号引用总结起来则包括了下面三类常量:

  • 类和接口的全限定名(即带有包名的Class名,如:org.lxh.test.TestClass)
  • 字段的名称和描述符(private、static等描述符)
  • 方法的名称和描述符(private、static等描述符)

    虚拟机在加载Class文件时才会进行动态连接,也就是说,Class文件中不会保存各个方法和字段的最终内存布局信息,因此,这些字段和方法的符号引用不经过转换是无法直接被虚拟机使用的。当虚拟机运行时,需要从常量池中获得对应的符号引用,再在类加载过程中的解析阶段将其替换为直接引用,并翻译到具体的内存地址中。

    这里说明下符号引用和直接引用的区别与关联:

  • 符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到了内存中。
  • 直接引用:直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那说明引用的目标必定已经存在于内存之中了。

    常量池中的每一项常量都是一个表,共有11种(JDK1.7之前)结构各不相同的表结构数据,没中表开始的第一位是一个u1类型的标志位(1-12,缺少2),代表当前这个常量属于的常量类型。11种常量类型所代表的具体含义如下表所示:

    这11种常量类型各自均有自己的结构。在CONSTANT_Class_info型常量的结构中有一项name_index属性,该常属性中存放一个索引值,指向常量池中一个CONSTANT_Utf8_info类型的常量,该常量中即保存了该类的全限定名字符串。而CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMeth

  • 43
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值