前面两个例子,一个简单的替换了二进制的编码,一个通过理解class的文件格式,可以增加输出的内容,都非常简单,但是实际可能用到的不会这么简单,更多的是对方法的操作,比如spring aop的实现方式有两种动态代理和字节码增强,其中字节码增强便可以通过修改class的二进制文件完成,另外对性能分析、调试跟踪和日志记录,也可以通过这种方式简单的实现,当然在现实中我们不会去真正的操作二进制码,我们一般通过第三方的库文件,比如:asm、 cglib、 serp、 和bcel等;但是他们的本质还是去操作二进制码;前面在分析helloworld.class 的时候,还剩下两块内容当时没有讨论(其实是一样的结构),这里我们先讨论这两块内容:
第一块: 第39点后面剩下的29个字节如下(请先回顾39点的内容):
0000014fh: 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 ; ........*?.?..
0000015fh: 01 00 0A 00 00 00 06 00 01 00 00 00 01 ; .............
Code_attribute 的定义:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
按照Code_attribute 的定义分析29个字符:
(1)、0X 00 01 表示max_stack;表示该方法执行的时候操作数栈最大的长度;这里表示操作数栈的长度为1;
(2)、0X 00 01表示 max_locals;表示方法局部变量所需要的空间的长度,
(3)、0X 00 00 00 05 表示code_length=5;即后面的5个字节为code的内容;
(4)、0X 2A B7 00 01 B1 :5个字节表示的便是code 的内容;
- 0X 2A :aload_0 表示将第一个引用类型本地变量推送至栈顶
-
0X B7 :invokespecial 调用超类方法,后面的0X 00 01,查看1号常量池 表示void init();所有表示调用超类的init方法
- 0X B1 return 返回void;
(5)、0X 00 00 :表示exception_table_length=0;也就是说没有异常处理;
(6)、0X 00 01 :表示attributes_count=1;接下来有一个attribute_info 的结构:
先复习一下attribute_info 的结构定义:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
1)、0X 00 0A :表示 attribute_name_index,查看10号常量池,为LineNumberTable ;查看LineNumberTable 属性的定义:
lineNumberTable_attribute{
U2 attribute_name_index;
U4 attribute_length;
U2 line_number_table_length
line_number_info line_number_table
}
line_number_info 的定义:
line_number_info {
U2 start_pc;
U2 line_number;
}
2)、0X 00 00 00 06 :表示attribute_length=6,
3)、0X 00 01 :表示line_number_table_length=1,即后面有一个line_number_info 结构
3.1)、0X 00 00 表示 start_pc; 新行开始时,代码数组的偏移量,该偏移量从代码数组的起始位置开始;
3.2)、0X 00 01 表示 line_number=1
LineNumberTable 中包含了一些调试信息,不做讨论;
到这里第一块结构就ok了;
第二块: 第41点后面剩下的37个字节如下(请先回顾41点的内容):这个结构和上面的第一块内容基本一致;
0000017ah: 00 02 00 01 00 00 00 09 B2 00 02 12 03 B6 00 04 ; ........?...?.
0000018ah: B1 00 00 00 01 00 0A 00 00 00 0A 00 02 00 00 00 ; ?..............
0000019ah: 03 00 08 00 04 ; .....
(1)、0X 00 02 表示max_stack;表示该方法执行的时候操作数栈最大的长度;这里表示操作数栈的长度为2;
(2)、0X 00 01 表示 max_locals;表示方法局部变量所需要的空间的长度
(3)、0X 00 00 00 09 表示code_length=5;即后面的9个字节为code的内容;
(4)、 B2 00 02 12 03 B6 00 04 B1 :9个字节表示的便是code 的内容;
该code[] 包含的实现该方法的JVM 的实际的字节,
- 0X B2 :getstatic 指令:表示获取指定类的静态域,并将其值压入栈顶,后面的0X 00 02 ;查看2号常量池,即将out(sysytem.out)压入栈
- 0X 12 :ldc:表示将一个常量池压入操作栈,后面的0X 03 便是这个操作数,查看第3号常量池,为hello world,我们要输出的内容;
- 0X B6 : invokevirtual,调用实例方法,后面的0X 00 04 ,查看4号常量池 表示java/io/PrintStream的println方法,这个指令弹出两个操作数,即是调用 out.print("hello world");
- 0X B1 : return ;返回void
(5)、0X 00 00 :表示exception_table_length=0;也就是说没有异常处理;
(6)、0X 00 01 :表示attributes_count=1;接下来有一个attribute_info 的结构:
1)、0X 00 0A :表示 attribute_name_index,查看10号常量池,为LineNumberTable ;
查 看LineNumberTable 属性的定义:
2)、0X 00 00 00 0A :表示attribute_length=10,
3)、0X 00 02 :表示line_number_table_length=2,即后面有一个line_number_info 结构
3.1)、0X 00 00 表示 start_pc;新行开始时,代码数组的偏移量,该偏移量从代码数组的起始位置开始;
3.2)、0X 00 03 表示 line_number=3
3.3)、0X 00 08 表示 start_pc;新行开始时,代码数组的偏移量,该偏移量从代码数组的起始位置开始;
3.4)、0X 00 04 表示 line_number=4
LineNumberTable 中包含了一些调试信息,不做讨论;
至此 整个helloworld的class 文件全部分析好了,
另外:
1、文章中提高的指令集参照 http://thinkinmylife.iteye.com/blog/443900
2、对在class 复合数据结构中的 9中属性没有做详细的介绍,只是对用到的code_attribute,sourcefile_attribute,linenumbertable_attribute ,有一个简单的说明,其他还有sythetic、localvariabletable、innerclass、exception、deprecated、constantvalue几种;