您的位置:首页 > 编程语言 > Java开发

JVM规范研读-3 Class文件格式

2014-08-22 16:37 309 查看
每个 Class文件都是由8字节为单位的字节流组成,所有的16位、32位和64位长度的数据将被构造成2个、4个和8个8字节单位来表示。多字节数据项总是按照Big-Endian1的顺序进行存储。在JavaSDK中,访问这种格式的数据可以使用java.io.DataInput、java.io.DataOutput等接口和java.io.DataInputStream和java.io.DataOutputStream等类来实现。Big-Endian顺序是指按高位字节在地址最低位,最低字节在地址最高位来存储数据Class File结构体
ClassFile {
u4 magic;//魔数 ,0xCAFEBABE
u2 minor_version;//某个 Class 文件的主版本号为 M,副版本号为 m,那么这个 Class 文件的格式版本号就确定为 M.m
u2 major_version;
u2 constant_pool_count;//常量池计数器,constant_pool_count 的值等于 constant_pool 表中的成员数加 1
cp_info constant_pool[constant_pool_count-1];//字符串常量、类或接口名、字段名和其它常量,第一个字节是flag,标记类型用的。
u2 access_flags;//掩码标志,用于表示某个类或者接口的访问权限及基础属性。
u2 this_class;//表明类型或接口,一般是个全限定形式java.lang.Object
u2 super_class;//父类,0表示是Object对象
u2 interfaces_count;//接口计数器
u2 interfaces[interfaces_count];
u2 fields_count;//字段计数器
field_info fields[fields_count];
u2 methods_count;//方法计数器
method_info methods[methods_count];
u2 attributes_count;//属性计数器
attribute_info attributes[attributes_count];
}接口类必须是abstract,但是不能被标记为final、super、enum类型注解类型必须是interface的final与abstract互斥ACC_SUPER 标志用于确定该Class文件里面的invokespecial指令使用的是哪一种执行语义。 是init还是调用super?this_class的值必须是对 constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量,表示这个Class文件所定义的类或接口super_class的值必须为 0或者是对constant_pool表中项目的一个效索引值。如果它的值不为0,那constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量2 语法符号
FieldType:
BaseType
ObjectType
ArrayType
MethodDescriptor:
( ParameterDescriptor* ) ReturnDescriptor
方法描述符参数描述符:
ParameterDescriptor:
FieldType
返回值描述符:[code]ReturnDescriptor:
FieldType
VoidDescriptor
其中 VoidDescriptor 表示当前方法无返回值,即返回类型是 void。符号如下(字符 V 即void):   VoidDescriptor:
V
方法的参数列表总长度小于等于 255 ,如果是实例方法,考虑this指针在0号位long和double占2位,其余都是1位[/code]
字段描述符
FieldDescriptor:FieldType
ComponentType:FieldType
FieldType:
BaseType
ObjectType
    ArrayTypeBaseType:
B:byteC:charD:doubleF:floatI:intJ:longS:shortZ:booleanObjectType:LClassname ;ArrayType:[ComponentType描述 int实例变量的描述符是“I”;java.lang.Object的实例描述符是“Ljava/lang/Object;”double的三维数组“double d[][][];”的描述符为“[[[D”。举个栗子:
Object mymethod(int i, double d, Thread t)的描述符为:
   (IDLjava/lang/Thread;)Ljava/lang/Object;

3 常量池item

所有的常量池项都具有如下通用格式:cp_info {[code]    u1 tag;类型
u1 info[];}常量池中,每个 cp_info 项的格式必须相同,它们都以一个表示 cp_info 类型的单字节“tag”项开头。后面 info[]项的内容 tag 由的类型所决定。每个 tag 项必须跟随 2个或更多的字节,这些字节用于给定这个常量的信息,附加字节的信息格式由 tag 的值决定。[/code]
CONSTANT_Class类或接口7
[code]CONSTANT_Class_info {u1 tag;
    u2 name_index;//对常量池的一个有效索引 ,且指向的是一个CONSTANT_Utf8_info类型}
举个栗子表现二维 int 数组类型int[][]的名字是:[[I表示一维 Thread 数组类型Thread[]的名字是:[Ljava/lang/Thread;下面是字段,方法和接口方法由类似的结构CONSTANT_Fieldref域指针
9
CONSTANT_Methodref方法引用10
CONSTANT_InterfaceMethodref接口方法引用结构都一样:
     u1 tag;
     u2 class_index;
     u2 name_and_type_index;指向的常量池索引项必须是CONSTANT_NameAndType_info
11
CONSTANT_String字符串表示String类型的常量对象:
CONSTANT_String_info {u1 tag;
    u2 string_index;//表示CONSTANT_Utf8_info(}
8
CONSTANT_Integer3
CONSTANT_Float4
CONSTANT_Long5
CONSTANT_Double6
long和double类型的常量CONSTANT_Long_info{ u1 tag; u4 high_bytes; u4 low_bytes;}CONSTANT_Double_info { u1tag; u4 high_bytes; u4 low_bytes;}CONSTANT_NameAndType值12
表示字段或方法格式
    u1 tag;
    u2 name_index;//指向的索引项必须是 CONSTANT_Utf8_info
    u2 descriptor_index;//字段或方法描述符,CONSTANT_Utf8_info
格式:u1 tag; u2 length; u1 bytes[length];CONSTANT_Utf8
1
CONSTANT_Utf8_info {u1 tag;
u2 length;//表示bytes数组的长度 u1 bytes[length];}[/code]bytes[]是表示字符串值的 byte 数组,bytes[]数组中每个成员的 byte 值都不会是 0,也不在 0xf0 至 0xff 范围内。CONSTANT_MethodHandle方法句柄
    u1 tag;u1 reference_kind;u2 reference_index;
reference_kind 项的值必须在 1 至 9 之间(包括 1 和 9)reference_index 项的值必须是对常量池的有效索引:略复杂,根据kind的类型,index取值有限制:如果 reference_kind 项的值为 1(REF_getField)、2(REF_getStatic)、3(REF_putField)或 4(REF_putStatic),那么常量池在 reference_index索引处的项必须是 CONSTANT_Fieldref_info(§4.4.2)结构,表示由一个字段创建的方法句柄。 如果 reference_kind 项的值是 5(REF_invokeVirtual)、6(REF_invokeStatic)、7(REF_invokeSpecial)或 8(REF_newInvokeSpecial),那么常量池在 reference_index 索引处的项必须是 CONSTANT_Methodref_info(§4.4.2)结构,表示由类的方法或构造函数创建的方法句柄。 如果 reference_kind 项的值是 9(REF_invokeInterface),那么常量池在reference_index 索引处的项必须是 CONSTANT_InterfaceMethodref_info(§4.4.2)结构,表示由接口方法创建的方法句柄。 如果 reference_kind项的值5(REF_invokeVirtual)、6(REF_invokeStatic)、7(REF_invokeSpecial)或 9(REF_invokeInterface),那么方法句柄对应的方法不能为实例初始化(<init>)方法或类初始化方法(<clinit>)。 如果 reference_kind 项的值是 8(REF_newInvokeSpecial),那么方法句柄对应的方法必须为实例初始化(<init>)方法。
15
CONSTANT_MethodType方法类型方法类型
    u1 tag;
u2 descriptor_index;//必须指向CONSTANT_UTF8_INFO
16
CONSTANT_InvokeDynamic18
用于表示 invokedynamic 指令所使用到的引导方法(Bootstrap Method)、引导方法使用到动态调用名称(Dynamic Invocation Name)、参数和请求返回类型、以及可以选择性的附加被称为静态参数(Static Arguments)的常量序列。
结构
    u1 tag;
    u2 bootstrap_method_attr_index;//必须是对当前 Class 文件中引导方法表的 bootstrap_methods[]数组的有效索引。
    u2 name_and_type_index;//必须指向CONSTANT_NameAndType_info
[/code]

4 字段

field_info {u2 access_flags;//PUBLIC、PRIVATE、PROTECTED、STATIC、FINAL、VOLATILE、TRANSIENT、SYNTHETIC、ENUM
    u2 name_index;//指向constant_utf8_info,表示一个有效的字段的非全限定名u2 descriptor_index;//指向constant_utf8_info,表示一个有效的字段的描述符u2 attributes_count;//附加属性的数量attribute_info attributes[attributes_count];
}SYNTHETIC表示是编译器自动生成的不能同时设置标志 ACC_FINAL和ACC_VOLATILE接口中的所有字段都具有 ACC_PUBLIC,ACC_STATIC和ACC_FINAL标记,也可能被设置ACC_SYNTHETIC标记,但是不能含有其它attributes可以是ConstantValue, Synthetic, Signature,Deprecated,RuntimeVisibleAnnotations和RuntimeInvisibleAnnotations

5 方法

method_info {u2 access_flags;
u2 name_index;//constant_utf8_info,表示初始化方法的名字(<init>或<clinit>)或表示一个方法的有效的非全限定名
    u2 descriptor_index;//表示方法的描述符u2 attributes_count;//附加属性的个数attribute_info attributes[attributes_count];
}标记可以是public/private/protected/static/final/synchronized/bridge/varages/native/abstract/strcit/synthetic其中bridge是由编译器生成的桥接方法,native是本地方法,strcit指方法使用FP-strict,varages表示方法参数是变长的如果方法是ABSTRACT标志,则这个方法不能被设置 ACC_FINAL,ACC_NATIVE, ACC_PRIVATE, ACC_STATIC,ACC_STRICT和 ACC_SYNCHRONIZED标志 。接口方法必须被设置 ACC_ABSTRACT和ACC_PUBLIC标志;还可以选择设置ACC_VARARGS,ACC_BRIDGE和ACC_SYNTHETIC标志,但是不能再设置其它标识了。类初始化方法由Java虚拟机隐式自动调用,它的 access_flags项的值除了ACC_STRICT标志,其它的标志都将被忽略。属性可以是Code,Exceptions,Synthetic,Signature,Deprecated,untimeVisibleAnnotations,RuntimeInvisibleAnnotations(,RuntimeVisibleParameterAnnotations,RuntimeInvisibleParameterAnnotations和AnnotationDefault结构。

6 属性

attribute_info {u2 attribute_name_index;u4 attribute_length;u1 info[attribute_length];
}被预定义的 Class文件属性 ,不能再被属性表中其他的自定义属性所使用。ConstantValue 常量字段,定长属性,位于field_info结构的属性表中。
Code 变长属性1.0.245.3
StackMapTable(§4.7.4)650.0
Exceptions(§4.7.5)1.0.245.3
InnerClasses(§4.7.6)1.145.3
EnclosingMethod(§4.7.7)5.049.0
Synthetic(§4.7.8)1.145.3
Signature(§4.7.9)5.049.0
SourceFile(§4.7.10)1.0.245.3
SourceDebugExtension(§4.7.11)5.049.0
LineNumberTable(§4.7.12)1.0.245.3
LocalVariableTable(§4.7.13)1.0.245.3
LocalVariableTypeTable(§4.7.14)5.049.0
Deprecated(§4.7.15)1.145.3
RuntimeVisibleAnnotations(§4.7.16)5.049.0
RuntimeInvisibleAnnotations(§4.7.17)5.049.0
RuntimeVisibleParameterAnnotations(§4.7.18)5.049.0
RuntimeInvisibleParameterAnnotations(§4.7.19)5.049.0
AnnotationDefault(§4.7.20)5.049.0
BootstrapMethods(§4.7.21)
6.1 ConstantValue属性的结构如下
ConstantValue_attribute {u2 attribute_name_index;u4 attribute_length;定长为2u2 constantvalue_index;必须是一个对常量池的有效索引
}常量池在该索引处的项给出该属性表示的常量值。常量池的项的类型表示的字段类型如下:ConstantValue属性的类型
ConstantValue_attribute {u2 attribute_name_index;u4 attribute_length;u2 constantvalue_index;
}
字段类型项类型
longCONSTANT_Long
floatCONSTANT_Float
doubleCONSTANT_Double
int,short,char,byte,booleanCONSTANT_Integer
StringCONSTANT_String
6.2 Code属性Code 属性是一个变长属性,位于method_info结构的属性表一个 Code属性只为唯一一个方法、实例类初始化方法或类初始化方法保存Java虚拟机指令及相关辅助信息。如果方法被声明为 native或者abstract类型,那么对应的 method_info结构不能有明确的 Code属性,其它情况下,method_info有必须有明确的 Code属性。格式Code_attribute {u2 attribute_name_index;表示字符串“Code”u4 attribute_length;当前属性的长度,不包括开始的6个字节。u2 max_stack;当前方法的操作数栈在运行执行的任何时间点的最大深度u2 max_locals;当前方法引用的局部变量表中的局部变量个数,long 和double型的局部变量的最大索引是 max_locals-2,其它类型的局部变量的最大索引是max_locals-1.u4 code_length;//code_length项给出了当前方法的code[]数组的字节数。由于部分指令在 code[]数组中存有直接操作数,换句话说,有一些字节码指令的实际长度是超过一个字节的,因此此处字节数长度code_length并不等同于 code[]数组的成员个数。u1 code[code_length];u2 exception_table_length;{u2 start_pc; //异常处理器在code[]数组中的有效范围
            u2 end_pc;//u2 handler_pc;//异常处理器的起点u2 catch_type;//0表示finally,非0表示catch的exception
} exception_table[exception_table_length];//异常处理器
当程序计数器在范围[start_pc, end_pc)内时,异常处理器就将生效。即设 x 为异常句柄的有效范围内的值,x 满足:start_pc ≤ x < end_pc。u2 attributes_count;attribute_info attributes[attributes_count];
}6.3 StackMapTable属性栈映射帧包含0至多个栈映射帧(Stack Map Frames)每个栈映射帧都显式或隐式地指定了一个字节码偏移量,用于表示局部变量表和操作数栈的验证类型如何从方法的局部变量和操作数栈的存储单元映射到验证类型StackMapTable属性的格式如下:
StackMapTable_attribute {u2 attribute_name_index;u4 attribute_length;u2 number_of_entries;
stack_map_frame entries[number_of_entries];//Entries 表的每个成员是都是一个 stack_map_frame 结构的项。
}每个 stack_map_frame结构都使用一个特定的字节偏移量来表示类型状态。每个帧类型(Frame Type)都显式或隐式地标明一个offset_delta(增量偏移量)值,用于计算每个帧在运行时的实际字节码偏移量。使用时帧的字节偏移量计算方法为:前一帧的字节码偏移量(BytecodeOffset)加上offset_delta的值再加1,如果前一个帧是方法的初始帧(InitialFrame),那这时候字节码偏移量就是offset_delta。在 Code属性的 code[]数组项中,如果偏移量i 的位置是某条指令的起点,同时这个Code属性包含有StackMapTable属性,它的 entries项中也有一个适用于地址偏移量 i的stack_map_frame结构,那我们就说这条指令拥有一个与之相对应的栈映射帧。一个栈映射帧可以包含若干种帧类型(Frame Types): page122,实在没看懂
union stack_map_frame {same_frame;
same_locals_1_stack_item_frame;
same_locals_1_stack_item_frame_extended;
chop_frame;
   same_frame_extended;append_frame;full_frame;
}6.4 Exceptions属性Exceptions属性指出了一个方法需要检查的可能抛出的异常
Exceptions_attribute {u2 attribute_name_index;//指向常量池,字符串"Exceptions"u4 attribute_length;u2 number_of_exceptions;u2 exception_index_table[number_of_exceptions];
}常量池在这些索引处的成员必须都是 CONSTANT_Class_info结构,表示这个方法声明要抛出的异常的类的类型。一个方法如果要抛出异常,必须至少满足下列三个条件中的一个: 要抛出的是RuntimeException或其子类的实例。 要抛出的是Error或其子类的实例。 要抛出的是在exception_index_table[]数组中申明的异常类或其子类的实例。这些要求没有在Java虚拟机中进行强制检查,它们只在编译时进行强制检查。6.5 InnerClasses 属性 ,内部类和内部接口定义一个表示类或接口的 Class格式为C。如果C 的常量池中包含某个CONSTANT_Class_info成员,且这个成员所表示的类或接口不属于任何一个包,那么C 的ClassFile结构的属性表中就必须含有对应的 InnerClasses属性。在JDK 1.1中为了支持内部类和内部接口而引入的。InnerClasses_attribute {u2 attribute_name_index;//表示字符串"InnerClasses"u4 attribute_length;u2 number_of_classes;{ u2 inner_class_info_index;
u2 outer_class_info_index;0表示顶层类,接口,局部类,匿名类,
		否则代表一个类或接口,C 为这个类或接口的成员。u2 inner_name_index;0表示匿名类u2 inner_class_access_flags;内部类访问标记
    	} classes[number_of_classes];}
常量池中的每个 CONSTANT_Class_info结构如果表示的类或接口并非某个包的成员,则每个类或接口在 classes[]数组中都有一个成员与之对应。如果 Class中包含某些类或者接口,那么它的常量池必须包含这些成员,即使某些类或者接口没有被这个Class 使用过。
内部类访问全和基础属性标志
标记名含义
ACC_PUBLIC0x0001源文件定义 public
ACC_PRIVATE0x0002源文件定义 private
ACC_PROTECTED0x0004源文件定义 protected
ACC_STATIC0x0008源文件定义 static
ACC_FINAL0x0010源文件定义 final
ACC_INTERFACE0x0200源文件定义 interface
ACC_ABSTRACT0x0400源文件定义 abstract
ACC_SYNTHETIC0x1000声明 synthetic,非源文件定义
ACC_ANNOTATION0x2000声明 annotation
ACC_ENUM0x4000声明 enum
写不下去了。。。to be continued...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jvm java