本文主要参考了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 1
此时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