剖析Java Class文件结构

 

本文主要参考了http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html

ClassFile {

u4 magic; // 必须为: 0xCAFEBABE

u2 minor_version;

u2 major_version; //CLASS文件结构主次版本号 JAVA2支持45.0-46.0

u2 constant_pool_count; //记录常量信息

cp_info constant_pool[constant_pool_count-1]; //计数从1开始

u2 access_flags; //class/interface访问权限

u2 this_class; //指向constant_poll中的有效索引值

u2 super_class; //0或指向constant_poll中的有效索引值,对于interface必须为非0

u2 interfaces_count; //superinterfaces的个数

u2 interfaces[interfaces_count]; //计数[0,count-1) 对应constant_pool中的一个索引值

u2 fields_count; 

field_info fields[fields_count]; //主要用于记录class及实例中的变量

u2 methods_count;

method_info methods[methods_count];

u2 attributes_count;

attribute_info attributes[attributes_count];

}

 

cp_info {

u1 tag; 

u1 info[]; 

Note:cp_info相当于一个父类的引用类型,生成Class文件时根据tag来决定使用哪种具体的结构,完全可以不考虑info代表什么。tag及其具体的结构如下:

后面的attribute_info的用法和cp_info类似!

tag 意义如下: 

CONSTANT_Class                  7    

        CONSTANT_Fieldref                9  

        CONSTANT_Methodref              10  

        CONSTANT_InterfaceMethodref      11  

        CONSTANT_String                 8  

        CONSTANT_Integer                3  

        CONSTANT_Float                  4  

        CONSTANT_Long                   5  

        CONSTANT_Double                 6  

        CONSTANT_NameAndType           12  

        CONSTANT_Utf8                   

此时cp_info分别对应结构变化为

1. CONSTANT_Class 

          CONSTANT_Class_info {

                //必须为CONSTANT_Class (7)

u1 tag;

//为指向constant_pool中的一个索引值,

//指向的元素的cp_info等价为CONSTANT_Utf8_info 

                u2 name_index;          

  }

        

        2. CONSTANT_Fieldref

          CONSTANT_Fieldref_info {

                    u1 tag;

//constant_pool的索引,对应CONSTANT_Class_info

                    u2 class_index;   

//constant_pool的索引,对应CONSTANT_NameAndType_info 

                    u2 name_and_type_index;

          }

 

        3. CONSTANT_Methodref

           CONSTANT_Methodref_info {

                    u1 tag;

                    u2 class_index;

                    u2 name_and_type_index;

           }

 

        4. CONSTANT_InterfaceMethodref

           CONSTANT_InterfaceMethodref_info {

                    u1 tag;

                    u2 class_index;

                    u2 name_and_type_index;

           }

 

        5. CONSTANT_String

           CONSTANT_String_info {

                    u1 tag;

                    u2 string_index;

            }

 

        6. CONSTANT_Integer

            CONSTANT_Integer_info {

                    u1 tag;

                    u4 bytes;

            }

  

        7. CONSTANT_Float

           CONSTANT_Float_info {

                    u1 tag;

                    u4 bytes;

           }

 

        8. CONSTANT_Long

            CONSTANT_Long_info {

                    u1 tag;

                    u4 high_bytes;

                    u4 low_bytes;

             }

   

        9. CONSTANT_Double

           CONSTANT_Double_info {

                    u1 tag;

                    u4 high_bytes;

                    u4 low_bytes

           }

 

        10.CONSTANT_NameAndType

            CONSTANT_NameAndType_info {

                    u1 tag;

                    u2 name_index;

                    u2 descriptor_index;

            }

 

        11.CONSTANT_Utf8

            CONSTANT_Utf8_info {

                    u1 tag;

                    u2 length;

                    u1 bytes[length];

            }

access_flags意义如下:

ACC_PUBLIC        0x0001  

        ACC_FINAL         0x0010  

        ACC_SUPER         0x0020  

        ACC_INTERFACE     0x0200  

        ACC_ABSTRACT      0x0400   

如果是interface那么必须置ACC_INTERFACE,如果没有置ACC_INTERFACE则定义的是一个类而非接口。如果设置了ACC_INTERFACE,那么ACC_ABSTRACT位也必须被设置,当然也可以设置ACC_PUBLIC。ACC_SUPER用以表明invokespecial语义,Sun公司老的JAVA编译器没有设置ACC_SUPER,并且老的JVM忽略ACC_SUPER位,但新的编译器应该实现invokespecial语义。其他未指明的位保留将来使用,并且编译器应当将其置为0,同时Java虚拟机应当忽略他们。

this_class: constant_pool中的索引值,指向的元素的cp_info等价为CONSTANT_Class_info

字段field_info表的格式

field_info {

u2 access_flags; //访问控制权

u2 name_index; //constant_pool中的索引,对应于CONSTANT_Utf8_info描述。

u2 descriptor_index; //constant_pool中的索引,对应于CONSTANT_Utf8_info描述。

u2 attributes_count;

attribute_info attributes[attributes_count]; //attribute_info将在mothods后描述。

}

field_info中access_flages意义如下:

ACC_PUBLIC    0x0001

ACC_PRIVATE    0x0002

ACC_PROTECTED   0x0004

ACC_STATIC    0x0008

ACC_FINAL    0x0010

ACC_VOLATILE   0x0040

ACC_TRANSIENT   0x0080

其中很显然不能同时为ACC_FINAL和ACC_VOLATILE ;且前三项是互斥的。

interface必须置ACC_PUBLIC, ACC_STATIC,ACC_FINAL位,且不能置其他位。

其他未指明的位保留将来使用,并且编译器应当将其置为0,同时Java虚拟机应当忽略他们。

 

methods指明了类中的所有方法。method_info表的格式如下:

method_info {

u2 access_flags;

u2 name_index; //指向constant_pool的入口,对应为CONSTANT_Utf8_info 

u2 descriptor_index; //指向constant_pool的入口,对应为CONSTANT_Utf8_info

u2 attributes_count;

attribute_info attributes[attributes_count];

//此处只能出现Code、Exceptions、Synthetic、Deprecated四种类型的属性

}

access_flags访问权描述如下:

   ACC_PUBLIC            0x0001 

            ACC_PRIVATE          0x0002 

            ACC_PROTECTED       0x0004 

            ACC_STATIC          0x0008 

            ACC_FINAL           0x0010 

            ACC_SYNCHRONIZED    0x0020 

            ACC_NATIVE          0x0100 

            ACC_ABSTRACT        0x0400 

            ACC_STRICT          0x0800   

 

Java虚拟机规范的各种属性

attribute_info {

u2 attribute_name_index; //constant_pool中的索引,对应于CONSTANT_Utf8_info描述。

u4 attribute_length;

u1 info[attribute_length];

}

Note:attribute_info表与cp_info一样当作一个父类的引用类型,生成Class文件时会根据attribute_name_index对应的属性值来决定到底使用哪一种具体的属性格式。具体的属性格式如下:

现在已经预定义的属性有:

1. Code : attribute_info被替代为:

Code_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 max_stack; //执行此函数时可用的栈的最大深度

u2 max_locals; //执行此函数可用到的最大本地变量数目,包括参数。

// 注意:一个long/double相当于2个变量数目.

u4 code_length; //本函数用到的代码长度。

u1 code[code_length]; //实现本函数的真正字节码

u2 exception_table_length;

u2 start_pc;

u2 end_pc; //捕获违例时执行代码数组中的[start_pc, end_pc)部分

u2 handler_pc; //现在还不大明白他是干嘛的!!

u2 catch_type; //指向constant_pool的索引,对应CONSTANT_Class_info 

}exception_table[exception_table_length];

u2 attributes_count;

attribute_info attributes[attributes_count];

}

 

2. ConstantValue : attribute_info被替代为:

ConstantValue_attribute {

u2 attribute_name_index;

u4 attribute_length; //必须为2

u2 constantvalue_index;

}

对于constantvalue_index意义如下:

long        CONSTANT_Long 

float        CONSTANT_Float 

double        CONSTANT_Double 

int, short, char, byte, boolean   CONSTANT_Integer 

String        CONSTANT_String

ConstantValue用于field_info 中,用于描述一个static常量,此时field_info的access_flags应为ACC_STATIC 

 

3. Deprecated : attribute_info被替代为:

Deprecated_attribute {

u2 attribute_name_index;

u4 attribute_length; //必须为0

}

 

4. Exceptions : attribute_info被替代为:

Exceptions_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 number_of_exceptions;

u2 exception_index_table[number_of_exceptions];

}

 

5. InnerClasses : attribute_info被替代为:

InnerClasses_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 number_of_classes;

u2 inner_class_info_index; 

u2 outer_class_info_index; 

u2 inner_name_index; 

u2 inner_class_access_flags; 

} classes[number_of_classes];

}

 

6. LineNumberTable : attribute_info被替代为:

LineNumberTable_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 line_number_table_length;

u2 start_pc; //代码数组中的开始处

u2 line_number; //源文件中的行号(对于每一非空行都有这么一项) 

} line_number_table[line_number_table_length];

}

LineNumberTable用于Code属性中,通常用于调试。

 

7. LocalVariableTable : attribute_info被替代为:

LocalVariableTable_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 local_variable_table_length;

u2 start_pc;

u2 length; //当解释到代码数组的[start_pc,start_pc+length]时变量必须被赋值??

u2 name_index;

u2 descriptor_index;

u2 index; //到本地变量数组的一个索引

} local_variable_table[local_variable_table_length];

 

8. SourceFile : attribute_info被替代为:

SourceFile_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 sourcefile_index; //指向constant_pool中的一个CONSTANT_Utf8_info 结构。

}

 

9. Synthetic : attribute_info被替代为:

Synthetic_attribute {

u2 attribute_name_index; //不用废话了吧?

u4 attribute_length; //必须为0

}

Synthetic用在 field_info、 method_info 中,

一个没有出现在源程序中的变量必须使用Synthetic标记。

 

Note:以上9种属性是java 1.1版本时固有的属性,在java 1.5之后又添加了不少属性,列举如下:java1.5中添加的EnclosingMethod、Signature、SourceDebugExtension、LocalVariableTypeTable、RuntimeVisibleAnnotation、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations以及AnnotationDefault;java1.6中添加的StackMapTable属性。当然你也可以定义自己的属性,但要你自己的编译器和虚拟机实现。JVM将忽略自己不认可的属性。

 

接下来具体举例实践一下

源代码Act.java

class Act {  

    public static void doMathForever() { 

        int i = 0; 

        for (;;) { 

            i += 1; 

            i *= 2; 

        } 

    } 

}

 

D:/>javac Act.java   编译Act.java后得Act.class文件 

D:/>javap -verbose Act    利用javap工具反编译Act.class

获得Contant pool常量池

const #1 = class           #7;     //  Act

const #2 = class           #16;    //  Object

const #3 = Method          #2.#4;  //  java/lang/Object."<init>":()V

const #4 = NameAndType     #6:#5;//  "<init>":()V

const #5 = Asciz           ()V;

const #6 = Asciz           <init>;

const #7 = Asciz           Act;

const #8 = Asciz           Act.java;

const #9 = Asciz         Code;

const #10 = Asciz          ConstantValue;

const #11 = Asciz          Exceptions;

const #12 = Asciz          LineNumberTable;

const #13 = Asciz          LocalVariables;

const #14 = Asciz          SourceFile;

const #15 = Asciz          doMathForever;

const #16 = Asciz          java/lang/Object;


涉及两个方法的字节码

public static void doMathForever();

  Code:

   Stack=2, Locals=1, Args_size=0

   0:   iconst_0

   1:   istore_0

   2:   iinc    0, 1

   5:   iload_0

   6:   iconst_2

   7:   imul

   8:   istore_0

   9:   goto    2

  LineNumberTable:

   line 5: 0

   line 7: 2

   line 8: 5

   line 6: 9

 

Act();

  Code:

   Stack=1, Locals=1, Args_size=1

   0:   aload_0

   1:   invokespecial   #3; //Method java/lang/Object."<init>":()V

   4:   return

  LineNumberTable:

   line 2: 0

 

使用UltraEdit打开Act.class文件可查看Class文件字节流

CA FE BA BE 00 03 00 2D 00 11 07 00 07 07 00 10 ; .......-........

0A 00 02 00 04 0C 00 06 00 05 01 00 03 28 29 56 ; .............()V

01 00 06 3C 69 6E 69 74 3E 01 00 03 41 63 74 01 ; ...<init>...Act.

00 08 41 63 74 2E 6A 61 76 61 01 00 04 43 6F 64 ; ..Act.java...Cod

65 01 00 0D 43 6F 6E 73 74 61 6E 74 56 61 6C 75 ; e...ConstantValu

65 01 00 0A 45 78 63 65 70 74 69 6F 6E 73 01 00 ; e...Exceptions..

0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 ; .LineNumberTable

01 00 0E 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 ; ...LocalVariable

73 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00 ; s...SourceFile..

0D 64 6F 4D 61 74 68 46 6F 72 65 76 65 72 01 00 ; .doMathForever..

10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 ; .java/lang/Objec

74 00 20 00 01 00 02 00 00 00 00 00 02 00 09 00 ; t. .............

0F 00 05 00 01 00 09 00 00 00 30 00 02 00 01 00 ; ..........0.....

00 00 0C 03 3B 84 00 01 1A 05 68 3B A7 FF F9 00 ; ....;.....h;....

00 00 01 00 0C 00 00 00 12 00 04 00 00 00 05 00 ; ................

02 00 07 00 05 00 08 00 09 00 06 00 00 00 06 00 ; ................

05 00 01 00 09 00 00 00 1D 00 01 00 01 00 00 00 ; ................

05 2A B7 00 03 B1 00 00 00 01 00 0C 00 00 00 06 ; .*..............

00 01 00 00 00 02 00 01 00 0E 00 00 00 02 00 08 ; ................

 

逐步解析上述字节流

CAFEBABE       magic     

0003    3    minor_version  

002D    45    major_version

0011       17          constant_pool_count

注:以下为16个常量池,可把上文的Contant pool作为参照

【constant_pool[1]】

07    7    tag      //CONSTANT_Class_info

0007    7    name_index   //"Act"

【constant_pool[2]】

07    7    tag    //CONSTANT_Class_info

0010  16    name_index  //"java/lang/Object"

【constant_pool[3]】

0A    10    tag   //CONSTANT_Methodref_info

0002    1    class_index   //"java/lang/Object"

0004    4    name_and_type_index   //"<init>":()V

【constant_pool[4]】

0C    12    tag   //CONSTANT_NameAndType_info

0006    6    name_index   //"<init>"

0005    5    descriptor_index  //"()V"

【constant_pool[5]】

01      1    tag   //CONSTANT_Utf8_info

0003  3    length

282956  "()V"   bytes[length]

【constant_pool[6]】

01      1    tag   //CONSTANT_Utf8_info

0006  6    length

3C696E69743E "<init>" bytes[length]

【constant_pool[7]】

01   1    tag   //CONSTANT_Utf8_info

0003  3    length

416374  "Act"   bytes[length]

【constant_pool[8]】

01      1    tag    //CONSTANT_Utf8_info

000B  11    length

4163742E6A617661 "Act.java" bytes[length]

【constant_pool[9]】

01   1    tag   //CONSTANT_Utf8_info

0004  4    length

436F6465  "Code"  bytes[length]

【constant_pool[10]】

01    1    tag   //CONSTANT_Utf8_info

000D    13    length

436F6E7374616E7456616C7565  "ConstantValue"  bytes[length]

【constant_pool[11]】

01      1    tag   //CONSTANT_Utf8_info

000A  10    length

457863657074696F6E73 "Exceptions" bytes[length]

【constant_pool[12]】

01      1    tag   //CONSTANT_Utf8_info

000F  15    length

4C696E654E756D6265725461626C65 "LineNumberTable" bytes[length]

【constant_pool[13]】

01   1    tag   //CONSTANT_Utf8_info

000E  14    length

4C6F63616C5661726961626C6573 "LocalVariables"  bytes[length]

【constant_pool[14]】

01   1    tag   //CONSTANT_Utf8_info

000A  10    length

536F7572636546696C65 "SourceFile"  bytes[length] 

【constant_pool[15]】

01    1    tag   //CONSTANT_Utf8_info

000D  13    length

646F4D617468466F7265766572  "doMathForever"  bytes[length]

【constant_pool[16]】

01   1    tag   //CONSTANT_Utf8_info

0010  16    length

6A6176612F6C616E672F4F626A656374 "java/lang/Object" bytes[length] 

 

0020    access_flags

0001  1  this_class

0002  2  super_class

0000  0  interfaces_count

0000  0  fields_count

0002  2  methods_count

注:使用method_info表的格式

0009    access_flags

000F  15  name_index    //"doMathForever"

0005  5  descriptor_index  //"()V"

0001  1  attributes_count

 

methods[0].attributes[0]---Code属性

0009  9  attribute_name_index  //"Code"

00000030 48  length

0002  2  max_stack

0001  1  max_locals

0000000C 12  code_length

033B8400011A05683BA7FFF9  code[code_length]

0000  0  exception_table_length

注:doMathForever()方法的字节码

addr instruction  mnemonic

--  -----------  --------

0    03      iconst_0

1   3B    istore_0

2   840001   iinc 0 1

5   1A    iload_0

6   05    iconst_2

7   68    imul

8   3B    istore_0

9   A7FFF9   goto 2

sub-attribute----LineNumberTable属性

0001  1  attributes_count

000C  12  attribute_name_index  //"LineNumberTable"

00000012 18  attribute_length

0004  4  line_number_table_length

注:line_number_info表

hex  dec  name  what it refers to

---  ---  ----  -------------------

line_number_table[0]

0000  0  start_pc  iconst_0, istore_0

0005  5  line_number int i = 0;

 

line_number_table[1]

0002  2  start_pc  iinc 0 1

0007  7  line_number i += 1

 

line_number_table[2]

0005  5  start_pc  iload_0, iconst_2, imul, istore_0

0008  8  line_number i *= 2

 

line_number_table[3]

0009  9  start_pc  goto 2

0006  6  line_number for(;;) {

第一个方法doMathForever()结束。。。。。。

methods[1]

0000    access_flags

0006   6  name_index  //"<init>"

0005   5  descriptor_index  //"()V"

0001   1  attributes_count

methods[1].attributes[0]---Code属性

0009   9  attribute_name_index   //"Code"

0000001D 29  attribute_length

0001  1  max_stack

0001  1  max_locals

00000005 5  code_length

2AB70003B1  code[code_length]

0000  0  exception_table_length

注:<init>方法的字节码

pc instruction  mnemonic

-- -----------  --------

0  2A    aload_0

1 B70003  invokespecial #3 <Method java.lang.Object.<init>()V>

4   B1    return

sub-attribute-----LineNumberTable属性

0001  1  attributes_count

000C  12  attribute_name_index  //"LineNumberTable"

00000006 6  attribute_length

0001    1  line_number_table_length

注:line_number_info表

hex  dec  name  what it refers to

---  ---  ----  -----------------

0000  0  start_pc  aload_0, invokespecial #3, return

0002  2  line_number class Act {

第二个方法 <init>()结束。。。。。。

接下来为ClassFile文件本身的attributes项,即"SourceFile"属性

0001  1 attributes_count

000E  14 attribute_name_index  //"SourceFile"

00000002 2 attribute_length

0008  8 sourcefile_index   //"Act.java"

OK!Act类的ClassFile文件的字节流全部完成了,最后补充说明一下LineNumberTable属性,它是建立了字节码流偏移量和源代码行号之间的映射关系

----------------------------------

1

2 class Act {  

3  

4 public static void doMathForever() { 

5        int i = 0; 

6        for (;;) { 

7            i += 1; 

8            i *= 2; 

9        } 

10    } 

}

------------------------------------------------------------

0    03      iconst_0

1   3B    istore_0

2   840001   iinc 0 1

5   1A    iload_0

6   05    iconst_2

7   68    imul

8   3B    istore_0

9   A7FFF9   goto 2

产生的LIneNumberTable为:

start_pc  lineNumber

0     5

2    7

5    8

9    6

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值