深入理解JVM之类文件结构

        JVM的第五章是调优案例分析与实战需要大家上机编程进行具体体验,有代码的调优和工具的调优,有机会可以亲自上机试一试,JVM的第六章讲了类文件结构,明白了Class文件是Java虚拟机执行引擎的数据入口,也是Java技术体系的基础构成之一。了解Class文件的结构对后面进一步了解虚拟机执行引擎有很重要的意义。第六章详细讲了Class文件结构中的各个组成部分,以及每个部分的定义,数据结构和使用方法。本章通过一个Java代码以及它的Class文件样例,用实战的方式演示了Class的数据是如何存储和访问的。

        样例代码:

package com.pl.chap06;

public class TestClass {
	
	private int m;
	
	public int inc(){
		return m + 1;
	}
}
        Class文件:


一:概述

        不管从计算机诞生开始还是到现在,计算机都只认识0和1,所以我们编写的程序需要经过编译期翻译成0和1构成的二进制格式才能由计算机执行。但是由于近10年内虚拟机以及大量建立在虚拟机之上的程序语言如雨后春笋出现并蓬勃发展,将我们编写的程序编译成二进制本地机器码(Native Code)已不再是唯一选择,越来越多的程序语言选择了与操作系统和机器指令集无关的,平台中立的格式作为程序编译后的存储格式。

二:无关性的基石

        Java语言的诞生是基于CPU多指令集和多操作系统的,故Java在最初诞生时的口号就是“一次编写,到处运行(Write once,Run Anywhere)”。各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码(ByteCode)是构成平台无关性的基石。注意这里是平台无关性,但是随着技术发展,现在人们也开始关注了虚拟机的另外一种特性——语言无关性。


三、Class类文件的结构

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

        根据JVM规范的规定,Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构中只有两种数据类型:无符号数和表,后面的解析都要以这两种数据类型为基础。无符号数属于基本的数据类型,以u1,u2,u4,u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。是由多个无符号数或者其他作为数据项构成的符合数据类型,所有表都习惯性地以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表,它由表6-1所示的数据项构成。


        无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的格式,这时称这一系列连续的某一类型的数据为某一类型的集合。

         Class的结构不像XML等描述语言,由于他没有任何分割符号,所以在表6-1中的数据项,无论是顺序还是数量,甚至于数据存储的字节序(Byte Ordering,Class文件中字节序为Big-Endian)这样的细节,都是被严格限定的,哪个字节代表什么含义,长度是多少,先后顺序如何,都不允许改变。

         注意:任何一个Class文件都对应着唯一一个类或接口的定义信息,但反过来说,类或接口并不一定都得定义在文件里(譬如类或接口也可以通过类加载器直接生成)。

魔数与Class文件的版本

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

        紧接着魔数的4个字节存储的是Class文件的版本号:第5和第6个字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version)。  

        如上图所示开头的4个字节的十六进制表示是0xCAFEBABE,代表次版本号的第5个和第6个字节值为0x0000,而主版本号为0x0032,也即是十进制的50,该版本号说明这个文件是可以被JDK1.6或以上版本虚拟机执行的Class文件。

         注意:例如JDK1.1能支持版本号为45.0~45.65535的Class文件,无法执行版本号为46.0以上的Class文件,而JDK1.2则能支持45.0~46.65535的Class文件。JDK1.7的能支持可生成Class文件主版本号最大值为51.0。目前最新的JDK为1.8。


常量池

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

        由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值(constant_pool_count)(它的计数是从1开始而不是0)。


        如上图所示常量池容量为十六进制数0x0016,即十进制的22,这就代表常量池有21项常量,索引值范围为1~21。在Class文件格式规范制定之时,设计者将第0项空出来是有特殊考虑的,这样做的目的是在于满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义,这种情况下就可以把索引值置为0来表示。Class文件结构中只有常量池的容量计数是从1开始,对于其它集合类型,包括接口索引集合、字段表达集合、方法表集合等的容量计数一般都从0开始。


        常量池主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。

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

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

        常量池中的每一项常量都是一个表,在JDK1.7之前有11种结构各不相同的表结构数据,在JDK

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值