虚拟机类文件结构

1:Class文件的结构

1.1:Class类文件的结构

    class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎全部是程序运行的必要数据,没有空隙存在。当遇到需要占用8位字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储。
    Class文件以采用一种类似C语言结构体的伪结构来存储数据,这种伪结构中只有两种数据类型:无符号数和表。
    无符号数属于基本数据类型,已u1、u2、u4、u8来分别代表1,2,4,8个字节,可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
    表是由多个无符号数或者其它表作为数据项构成的复合数据类型。
    Class文件格式ClassFile结构体的C语言描述如下:

struct ClassFile
{
              u4 magic;                   //识别Class文件格式,具体值为0xCAFEBABE,
              u2 minor_version;           // Class文件格式副版本号,
              u2 major_version;           // Class文件格式主版本号,
              u2 constant_pool_count;     //  常数表项个数,
              cp_info **constant_pool;    // 常数表,又称变长符号表,
              u2 access_flags;            //Class的声明中使用的修饰符掩码,
              u2 this_class;              //常数表索引,索引内保存类名或接口名,
              u2 super_class;             //常数表索引,索引内保存父类名,
              u2 interfaces_count;        //超接口个数,
              u2 *interfaces;             //常数表索引,各超接口名称,
              u2 fields_count;            //类的域个数,
              field_info **fields;        //域数据,包括属性名称索引,域修饰符掩码等,
              u2 methods_count;           //方法个数,
              method_info **methods;      //方法数据,包括方法名称索引,方法修饰符掩码等,
              u2 attributes_count;         //类附加属性个数,
              attribute_info **attributes; //类附加属性数据,包括源文件名等。
};

参考这篇文章

  1. 魔数和class文件的版本
    每个Class文件的头4个字节称为魔数,它的作用是确定这个文件是否是一个能被虚拟机接受的Class文件(0xCAFFBABY 咖啡宝贝)。紧接着魔数的4个字节存储的是Class文件的版本号。
    这里写图片描述

  2. 常量池
        紧接着主次版本号后的是常量池入口,由于常量的数量不是固定,所以需要在常量池的入口处放置一项u2类型的数据,代表常量池容量计算值,与Java语言中的习惯不一样,这个容量计数是从1开始的,设计者将第0项常量值空出来是有特殊考虑的,这样做的目的在于满足后面某些指向常量池的索引值的数据在特定情况下想要表达“不引用任何一个常量池的目的”的含义。
        常量池中主要存放两大类常量:字面量(Literal)符号引用(Symbolic Reference)。字面量比较接近Java语言层面的常量概念,如文本字符串、声明为final的常量值等。而符号引用则属于编译原理方面的概念,包括三类常量: 类和接口的全限定名;字段和名称和描述符;方法的名称和描述符。
          表:常量池的项目类型

    类型标志描述
    CONSTANT_Utf8_info1Utf8编码的字符串
    CONSTANT_Integer_info3整形字面量
    CONSTANT_Float_info4浮点型字面量
    CONSTANT_Long_info5长整形字面量
    CONSTANT_Double_info6双精度浮点型字面量
    CONSTANT_Class_info7类或接口的符号引用
    CONSTANT_String_info8字符串类型字面量
    CONSTANT_Fieldref_info9字段的符号引用
    CONSTANT_Methoddref10类中方法的引用
    CONSTANT_InterfaceMethodref_info11接口中方法的引用
    CONSTANT_NameAndType_info12字段或方法的部分符号引用
    CONSTANT_Method_Handle15表示方法句柄
    CONSTANT_Method_type_info16标识方法类型
    CONSTANT_InvokeDynamic_info18标识一个动态方法调用点

    常量池是最繁琐的数据,因为针对每种常量类型都有自己的数据结构(这里不详细记录,针对不同常量的数据结构,可以再另行查询)。
    javap工具可以用来分析class文件。

3. 访问标志
    在常量池结束后,紧接着的两个字节是访问标志(access_flag),这个标志用来识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为Public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等。
    表:访问标志

标志名称标志值含义
ACC_PUBLIC0x0001是否为Public类型
ACC_FINAL0x0010是否声明为Final,只有类可以设置
ACC_SUPER0x0020是否允许使用InvokeSpecial字节码指令的新语义,InvokeSpecial指令的语义在JDK1.0.2发生过变化,为了区别这条指令的语义,JDK1.0.2以后编译出来的类的这个标志必须为真
ACC_INTERFACE0x0200标志这是一个接口
ACC_ABSTRACT0x0400是否为abstract类型,对于接口或者抽象类来说,此标志值为真,其他类为假
ACC_SYNTHETIC0x1000标识这个类并不是由用户代码生成的
ACC_ANNOTATION0x2000标识这是一个注解
ACC_ENUM0x4000标识这是一个美剧类型



4. 类索引、父类索引与接口索引集合
    类索引和父类索引都是一个u2类型的数据,而接口索引集合是一组u2类型的数据集合,class文件中由这三项数据来确定类的继承关系。
    类索引和父类索引引用的两个u2类型的索引值表示,它们各自指向一个类型为CONSTANT_Class_info的类描述符常量,通过这个常量中的索引值可以找到定义早 CONSTANT_Utf8_info类型的常量中的全限定名字字符串。

5. 字段表集合
    字段表用来描述接口或者类中申明的变量。字段包括类级变量和实例级变量,但不包括方法内部申明的局部变量。可以描述的信息有:字段的作用域(private public protected)、是实例变量还是类变量(static)、可变性(final)、并发可见性( volatile修饰符,是否强制从主内存读写)、可否被序列化( transient)、字段数据类型(基本类型、对象、数组)、字段名称。
    表:字段表结构

类型名称数量
u2access_flags1
u2name_index1
u2descriptor1
u2attributes_count1
attribute_infoattbutesattributes_count

    字段修饰符放在Access_Flags项目中:
    表:字段访问标志

标志名称标志值含义
ACC_PUBLIC0x0001字段是否是public
ACC_PRIVATE0x0002字段是否是private
ACC_PROTECTED0x0004字段是否是protected
ACC_STATIC0x0008字段是否是static
ACC_FINAL0x0010字段是否是final
ACC_VOLATILE0x0040字段是否是volatile
ACC_TRANSIENT0x0080字段是否transient
ACC_SYNTHETIC0x1000字段是否是编译器自动产生
ACC_ENUM0x4000字段是否是enum



    跟随access_flag标志后面分别是name_index和descriptor_index。它们都是对常量池的引用,分别代表着字段的简单名称以及字段和方法的描述符。其中,全限定名称和简单名称很好理解,例如:org/test/ThreadTest和方法init()的简单吃init。
    方法和字段的描述符就要复杂一些。
    表:描述符标识字符含义

标志字符含义
B基本类型byte
C基本类型char
D基本类型double
F基本类型float
I基本类型int
J基本类型long
S基本类型short
Z基本类型boolean
V特殊类型void
L对象类型Ljava/lang/object
[或者[[数组类型

用描述符来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照严格的顺序放在一组()之内


6. 方法表集合
    方法表集合基本上和字段表差不多,只在访问标志和属性表集合的可选项中存在差别。


7. 属性表集合
       其实就是虚拟机规范预定义的一些属性。Java虚拟机规范第二版中预定义了9种虚拟机应当识别的属性,在javase7规范中,已经存在21种。

1.2:字节码指令的介绍

       java虚拟机指令是由一个字节长度的、代表某种特定操作含义的数字(称为操作码)以及跟随其后的零至多个代表此操作所需参数(称为操作数)而构成。

1.2.1. 加载和存储指令
       加载存储指令用来将数据在栈帧中的局部变量表和操作数栈之间来回传输。

1.2.2. 运算指令
       运算或算数指令用来对两个 操作数栈行的值进行某种特定的运算,并把结果从新存入操作栈顶。

1.2.3. 类型转换指令
    类型转换指令用来将两种不同的数值类型进行相互转换,这些转换一般用来实现代码中的显示类型转换操作,或者从来处理字节码指令集中数据类型相关指令无法与数据类型一一对应的问题。

1.2.4. 对象创建与访问指令
    不同对象的创建指令。

1.2.5. 操作数栈管理指令
    如果普通数据结构堆栈那样,java虚拟机提供了直接操作操作数栈的指令。
1.2.6. 控制转移指令
    控制转移指令可以让java虚拟机有条件或者无条件地从指令的位置指令而不是控制转移指令的下一条指令继续执行程序,从概念模型上理解,可以认为控制转移指令就是在有条件或者无条件特修改寄存器的值。

1.2.7. 方法调用和返回指令
    调用各种方法的指令。

1.2.8. 异常处理指令
    在Java程序中显示抛出的异常都由athrow指令来实现,除了用throw语句显示抛出异常的情况除外,Java虚拟机还规定了运行时异常会在其他Java虚拟机指令检测到异常时自动抛出。

1.2.9. 同步指令
    java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor)来实现。例如 java代码中的synchronize关键字。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值