深入理解JAVA虚拟机之类文件结构

来源:互联网 发布:qq企业邮箱域名 编辑:程序博客网 时间:2024/06/05 17:06

深入理解JAVA虚拟机之类文件结构

  • 深入理解JAVA虚拟机之类文件结构
    • 类文件结构
      • Class 类文件结构
        • magic 魔数与class版本
        • 常量池cp_info
        • 访问标示access_flags
        • 类索引父类索引和接口集合
        • 字段表集合
        • 方法表集合
        • 属性表集合

类文件结构

java规范将*java语言规范*和*java虚拟机规范*拆分,可见设计者在设计之初就考虑了让其他语言运行在JVM虚拟机之上,就目前而言以及出现大量运行在java虚拟机之上的语言比如我们熟悉的Groovy,Scala,Jython.不过这些只是在语言层面上有不通之处,最终都需要编译成JVM支持的.class文件才能最终被java虚拟机加载和运行,虚拟机并不关心Class的来源是何种语言,因此了解Class文件是理解JAVA虚拟机的基本条件。

Class 类文件结构

  1. Class文件是一组以8位字节为基础单位的二进制流,中间没有任何分隔符
  2. Class文件格式采用一种类似C语言结构体的伪结构存储数据,伪结构中只有两种数据类型(无符号数和表)
  3. 无符号数属于基本数据类型以u1,u2,u4,u8来表示1个字节,2个字节,4个字节和8个字节的无符号数,无符号数可以描述数字,索引,数量值,或者按照UTF-8编码的字符串值
  4. 表是由多个无符号数或者其他表作为数据项构成的复合数据结构,一般习惯性都已_info结尾

Class文件格式-摘自《深入理解java虚拟机》

类型名称数量u4magic1u2minor_version1u2major_version1u2constant_pool_count1cp_infoconstant_poolconstant_pool_count - 1u2access_flags1u2this_class1u2super_class1u2interfaces_count1u2interfacesinterfaces_countu2fields_count1field_infofieldsfields_countu2methods_count1method_infomethodsmethods_countu2attribute_count1attribute_infoattributesattributes_count

接下来对class文件中每项做深入解释

magic 魔数与class版本

Class文件开头四个字节为魔数字,作用是确定这是一个合法的class文件,和拓展名类似,不过拓展名可以被用户篡改。
紧接着魔数的4个字节存储的是class的版本号,5,6字节是次版本号 minor version,7,8字节是主版本号 major version。JVM可以向下兼容,但是不能接受比自己版本高的class字节码。JAVA版本号从45开始,之后每个JDK大版本向上加1。

常量池(cp_info)

常量池是Class中的资源仓库,它是class文件结构中和其他项目关联最多的数据结构。也是占用class文件空间最大的项目之一。由于常量池的中常量数目不确定,所以需要在常量池入口放置一项u2 (constant_pool_count)代表常量池中常量计数。常量池中主要存放2大类常量字面量符号引用
字面量:类似于java语言层面的常量,如文本字符串,声明为final的常量值等
符号引用:1.类和接口的全限定名,2.字段的名称和描述符,3.方法的名称和描述符
常量池中每一个常量都是一个表,常量池中项目类型如下:引用自《深入理解JAVA虚拟机》

常量池中数据项类型类型标志类型描述CONSTANT_Utf81UTF-8编码的Unicode字符串CONSTANT_Integer3int类型字面值CONSTANT_Float4float类型字面值CONSTANT_Long5long类型字面值CONSTANT_Double6double类型字面值CONSTANT_Class7对一个类或接口的符号引用CONSTANT_String8String类型字面值CONSTANT_Fieldref9对一个字段的符号引用CONSTANT_Methodref10对一个类中声明的方法的符号引用CONSTANT_InterfaceMethodref11对一个接口中声明的方法的符号引用CONSTANT_NameAndType12对一个字段或方法的部分符号引用

这14个表都有一个共同特点。表开始第一位是一个u1类型的标示位(tag),代表当前这个常量属于哪种类型常量

访问标示(access_flags)

这个标示用来识别一些类或者接口层次的访问信息,包括这个Class是类还是接口,是否为public 是否为abstract,是否是final等。
标示位具体含义如下表

标志名
标志值
标志含义
针对的对像
ACC_PUBLIC
0x0001
public类型
所有类型
ACC_FINAL
0x0010
final类型
ACC_SUPER
0x0020
使用新的invokespecial语义
类和接口
ACC_INTERFACE
0x0200
接口类型
接口
ACC_ABSTRACT
0x0400
抽象类型
类和接口
ACC_SYNTHETIC
0x1000
该类不由用户代码生成
所有类型
ACC_ANNOTATION 
0x2000
注解类型
注解
ACC_ENUM  
0x4000
枚举类型
枚举

类索引,父类索引和接口集合

类索引(this_class),父类索引(super_class),接口集合(interfaces),其中类索引和父类索引都是u2类型,的索引值,只想一个类型为CONSTANT_class_info的常量,根据该常量又可以找到在CONSTANT_Utf8_info中的全局限定名的字符串值。对于接口集合入口第一项为u2计数器(由于java可以实现多接口,但是只能单继承,所以接口数目是不定的),而接口集合是一个u2数据类型的集合,标示接口的在常量池中CONSTANT_class_info索引值

字段表集合

字段表(field_info)用于描述接口或者类中声明的变量。字段包括了类变量和实例变量,不包括在方法内声明的局部变量。
描述一个字段主要包括 作用域,static修饰符,final修饰符,可见性 volite,是否序列化 transient,字段数据类型。因此很适合使用标示位来表示。而字段的名称和数据类型无法固定,这些只能引用常量池来表示。

字段访问标示如下

标志名称标志值含义ACC_PUBLIC0x00 01字段是否为publicACC_PRIVATE0x00 02字段是否为privateACC_PROTECTED0x00 04字段是否为protectedACC_STATIC0x00 08字段是否为staticACC_FINAL0x00 10字段是否为finalACC_VOLATILE0x00 40字段是否为volatileACC_TRANSTENT0x00 80字段是否为transientACC_SYNCHETIC0x10 00字段是否为由编译器自动产生ACC_ENUM0x40 00字段是否为enum

字段描述符标示和含义

标志符含义B基本数据类型byteC基本数据类型charD基本数据类型doubleF基本数据类型floatI基本数据类型intJ基本数据类型longS基本数据类型shortZ基本数据类型booleanV基本数据类型voidL对象类型

对于数组每一个维度使用一个前置的[ 来表示,比如string数组用 [Ljava/lang/Stirng。
用描述符来描述方法时,按照先参数列表,后返回值的顺序描述。参数列表安装参数的严格顺序放在一组小括号内。
比如void inc()的描述符为 ()V

方法表集合

Class文件存储格式中对方法的描述与对字段的描述几乎采用了完全一致的方式,方法表结构同字段表一样包含了访问标示符,名称索引,描述符索引,属性表索引。
由于方法的特殊性(方法内包含有用户的业务代码)。因此上述的结构只是标示了方法的定义信息,方法内的JAVA代码,经过编译器编译成字节码指令后,存放在方法属性表集合中的一个名为Code的属性里面,属性表是Class文件格式中最具有拓展性的一种数据结构。

属性表集合

在Class文件中,字段表,方法表都可以携带自己的属性集合表,用于描述某些场景专有的信息。
虚拟机规范预定义的部分属性如下:

属性名称

使用位置

含义

Code

方法名

JAVA代码编译成的字节码指令

ConstantValue

字段表

final关键字定义的常量值

Deprecated

类,方法表,字段表

被声明为deprecated的方法和字段

Exceptions

方法表

方法抛出的异常

InnerClasses

类文件

内部类列表

LineNumberTable

Code属性

Java源码的行号与字节码指令的对应关系

LocalVariableTable

Code属性

方法的局部变量描述

SourceFile

类文件

源文件名称

Synthetic

类,方法表,字段表

标识方法或字段为编译器自动生成的

原创粉丝点击