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

每个 Class文件都是由 8字节为单位的字节流组成,所有的 16位、32位和 64位长度的数据将被构造成 2个、4个和 88字节单位来表示。多字节数据项总是按照 Big-Endian1的顺序进行存储。在Java SDK中,访问这种格式的数据可以使用java.io.DataInputjava.io.DataOutput等接口和 java.io.DataInputStreamjava.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 表中的成员数加 
   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?

  1. this_class 的值必须是对 constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为 CONSTANT_Class_info类型常量,表示这个Class 文件所定义的类或接口 

  1. super_class 的值必须为 0或者是对 constant_pool表中项目的一个效索引值。如果它的值不为 0,那constant_pool表在这个索引处的项必须为 CONSTANT_Class_info类型常量 


2 语法符号

FieldType:
    BaseType
    ObjectType
    ArrayType

MethodDescriptor:
    ( ParameterDescriptor* ) ReturnDescriptor
方法描述符

参数描述符:

ParameterDescriptor:
FieldType
 
        
返回值描述符:
         
         
ReturnDescriptor:
    FieldType
    VoidDescriptor

              
              

其中 VoidDescriptor 表示当前方法无返回值,即返回类型是 void。符号如下(字符 V void):

   VoidDescriptor:
     V

方法的参数列表总长度小于等于 255 ,如果是实例方法,考虑this指针在0号位

long和double占2位,其余都是1位


字段描述符

FieldDescriptor:
    FieldType
ComponentType:
    FieldType
FieldType:
    BaseType

ObjectType

    ArrayType
BaseType:

B:byte

C:char

D:double

F:float

I:int

J:long

S:short

Z:boolean

ObjectType:
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 {

    u1 tag;类型

u1 info[];

常量池中,每个 cp_info 项的格式必须相同,它们都以一个表示 cp_info 类型的单字节“tag”项开头。后面 info[]项的内容 tag 由的类型所决定。每个 tag 项必须跟随 2个或更多的字节,这些字节用于给定这个常量的信息,附加字节的信息格式由 tag 的值决定。


         
         
      
 

CONSTANT_Class类或接口

  

7

 
 

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;//对常量池的一个有效索引 ,且指向的是一个CONSTANT_Utf8_info类型


}
举个栗子

  1. 表现二维 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;//表示
                     
                     
  1. CONSTANT_Utf8_info

}

  

8






 
 

CONSTANT_Integer

  

3

 
 

CONSTANT_Float

  

4

 
 

CONSTANT_Long

  

5

 
 

CONSTANT_Double

  

6

 
 
                 

long和double类型的常量

CONSTANT_Long_info {

    u1 tag;

    u4 high_bytes;

    u4 low_bytes;

}

CONSTANT_Double_info {

    u1 tag;

    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];
}
  • bytes[]是表示字符串值的 byte 数组,bytes[]数组中每个成员的 byte 值都不会是 0,也不在 0xf0 0xff 范围内。 




CONSTANT_MethodHandle方法句柄

    u1 tag;
    u1 reference_kind;
    u2 reference_index;
  1. reference_kind 项的值必须在 1 9 之间(包括 1 9

  1. reference_index 项的值必须是对常量池的有效索引: 

略复杂,根据kind的类型,index取值有限制:

  •   如果 reference_kind 项的值为 1(REF_getField)、2(REF_getStatic)、3

    (REF_putField)或 4(REF_putStatic),那么常量池在 reference_index索引处的项必须是 CONSTANT_Fieldref_info4.4.2)结构,表示由一个字段创建的方法句柄。

  •   如果 reference_kind 项的值是 5(REF_invokeVirtual)、6(REF_invokeStatic)、7(REF_invokeSpecial)或 8(REF_newInvokeSpecial),那么常量池在 reference_index 索引处的项必须是 CONSTANT_Methodref_info4.4.2)结构,表示由类的方法或构造函数创建的方法句柄。

  •   如果 reference_kind 项的值是 9(REF_invokeInterface),那么常量池在reference_index 索引处的项必须是 CONSTANT_InterfaceMethodref_info4.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_InvokeDynamic

  18 
用于表示 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 


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_FINALACC_VOLATILE 

接口中的所有字段都具有 ACC_PUBLIC,ACC_STATICACC_FINAL标记,也可能被设置 ACC_SYNTHETIC标记,但是不能含有其它 

attributes可以是ConstantValue, Synthetic, Signature,Deprecated, RuntimeVisibleAnnotationsRuntimeInvisibleAnnotations


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_ABSTRACTACC_PUBLIC标志;还可以选择设置ACC_VARARGS,ACC_BRIDGEACC_SYNTHETIC标志,但是不能再设置其它标识了。

类初始化方法Java 虚拟机隐式自动调用,它的 access_flags项的值除了 ACC_STRICT标志,其它的标志都将被忽略。

属性可以是
  • Code,Exceptions,Synthetic,Signature,Deprecated,untimeVisibleAnnotations,RuntimeInvisibleAnnotations(,RuntimeVisibleParameterAnnotations,RuntimeInvisibleParameterAnnotationsAnnotationDefault结构。

6 属性

attribute_info {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];

被预定义的 Class 文件属性 ,不能再被属性表中其他的自定义属性所使用。 

  ConstantValue 常量字段,定长属性,位于 field_info结构的属性表中。 

 

Code  变长属性

   

1.0.2

   

45.3

 
 

StackMapTable4.7.4)

   

6

   

50.0

 
 

Exceptions4.7.5)

   

1.0.2

   

45.3

 
 

InnerClasses4.7.6)

   

1.1

   

45.3

 
 

EnclosingMethod4.7.7)

   

5.0

   

49.0

 
 

Synthetic4.7.8)

   

1.1

   

45.3

 
 

Signature4.7.9)

   

5.0

   

49.0

 
 

SourceFile4.7.10)

   

1.0.2

   

45.3

 
 

SourceDebugExtension4.7.11)

   

5.0

   

49.0

 
 

LineNumberTable4.7.12)

   

1.0.2

   

45.3

 
 

LocalVariableTable4.7.13)

   

1.0.2

   

45.3

 
 

LocalVariableTypeTable4.7.14)

   

5.0

   

49.0

 
 

Deprecated4.7.15)

   

1.1

   

45.3

 
 

RuntimeVisibleAnnotations4.7.16)

   

5.0

   

49.0

 
 

RuntimeInvisibleAnnotations4.7.17)

   

5.0

   

49.0

 
 

RuntimeVisibleParameterAnnotations4.7.18)

   

5.0

   

49.0

 
 

RuntimeInvisibleParameterAnnotations4.7.19)

   

5.0

   

49.0

 
 

AnnotationDefault4.7.20)

   

5.0

   

49.0

 
 

BootstrapMethods4.7.21)

             

6.1  ConstantValue属性的结构如下
ConstantValue_attribute {
    u2 attribute_name_index;
    u4 attribute_length;定长为2
    u2 constantvalue_index;必须是一个对常量池的有效索引 

  • 常量池在该索引处的项给出该属性表示的常量值。常量池的项的类型表示的字段类型如下:

    1. ConstantValue属性的类型

    ConstantValue_attribute {
        u2 attribute_name_index;
        u4 attribute_length;
        u2 constantvalue_index;
    

    }

     

    字段类型

       

    项类型

     
     

    long                                                

       

    CONSTANT_Long

     
     

    float

       

    CONSTANT_Float

     
     

    double

       

    CONSTANT_Double

     
     

    int,short,char,byte,boolean

       

    CONSTANT_Integer

     
     

    String

       

    CONSTANT_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 结构的项。 
}

  1. 每个 stack_map_frame 结构都使用一个特定的字节偏移量来表示类型状态。每个帧类型

    (Frame Type)都显式或隐式地标明一个offset_delta(增量偏移量)值,用于计算每个帧在运行时的实际字节码偏移量。使用时帧的字节偏移量计算方法为:前一帧的字节码偏移量(Bytecode Offset)加上offset_delta的值再加1,如果前一个帧是方法的初始帧(Initial Frame),那这时候字节码偏移量就是offset_delta。 


  1. Code 属性的 code[]数组项中,如果偏移量 i 的位置是某条指令的起点,同时这个 Code属性包含有 StackMapTable 属性,它的 entries 项中也有一个适用于地址偏移量 i stack_map_frame 结构,那我们就说这条指令拥有一个与之相对应的栈映射帧。 

  1. 一个栈映射帧可以包含若干种帧类型(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结构,表示这个方法声明要抛出的异常的类的类型。 

  1. 一个方法如果要抛出异常,必须至少满足下列三个条件中的一个:
    要抛出的是 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[]数组中都有一个成员与之对应。 

  1. 如果 Class 中包含某些类或者接口,那么它的常量池必须包含这些成员,即使某些类或者接口没有被这个 Class 使用过。 
     


                      
                      

内部类访问全和基础属性标志

 

标记名

  

  

含义

 
 

ACC_PUBLIC

  

0x0001

  

源文件定义 public

 
 

ACC_PRIVATE

  

0x0002

  

源文件定义 private

 
 

ACC_PROTECTED

  

0x0004

  

源文件定义 protected

 
 

ACC_STATIC

  

0x0008

  

源文件定义 static

 
 

ACC_FINAL

  

0x0010

  

源文件定义 final

 
 

ACC_INTERFACE

  

0x0200

  

源文件定义 interface

 
 

ACC_ABSTRACT

  

0x0400

  

源文件定义 abstract

 
 

ACC_SYNTHETIC

  

0x1000

  

声明 synthetic,非源文件定义

 
 

ACC_ANNOTATION

  

0x2000

  

声明 annotation

 
 

ACC_ENUM

  

0x4000

  

声明 enum

 

写不下去了。。。


to be continued...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值