前面兩個例子,一個簡單的替換了二進制的編碼,一個通過理解class的文件格式,可以增加輸出的內容,都非常簡單,但是實際可能用到的不會這么簡單,更多的是對方法的操作,比如spring aop的實現(xiàn)方式有兩種動態(tài)代理和字節(jié)碼增強,其中字節(jié)碼增強便可以通過修改class的二進制文件完成,另外對性能分析、調試跟蹤和日志記錄,也可以通過這種方式簡單的實現(xiàn),當然在現(xiàn)實中我們不會去真正的操作二進制碼,我們一般通過第三方的庫文件,比如:asm、 cglib、 serp、 和bcel等;但是他們的本質還是去操作二進制碼;前面在分析helloworld.class 的時候,還剩下兩塊內容當時沒有討論(其實是一樣的結構),這里我們先討論這兩塊內容:
第一塊: 第39點后面剩下的29個字節(jié)如下(請先回顧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;表示該方法執(zhí)行的時候操作數(shù)棧最大的長度;這里表示操作數(shù)棧的長度為1;
(2)、0X 00 01表示?max_locals;表示方法局部變量所需要的空間的長度,
(3)、0X 00 00 00 05 表示code_length=5;即后面的5個字節(jié)為code的內容;
(4)、0X?2A B7 00 01 B1 :5個字節(jié)表示的便是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; 新行開始時,代碼數(shù)組的偏移量,該偏移量從代碼數(shù)組的起始位置開始;
? ? ? ?3.2)、0X 00 01 表示 line_number=1
?
LineNumberTable ?中包含了一些調試信息,不做討論;
到這里第一塊結構就ok了;
?
第二塊: 第41點后面剩下的37個字節(jié)如下(請先回顧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;表示該方法執(zhí)行的時候操作數(shù)棧最大的長度;這里表示操作數(shù)棧的長度為2;
(2)、0X 00 01 表示?max_locals;表示方法局部變量所需要的空間的長度
(3)、0X 00 00 00 09 表示code_length=5;即后面的9個字節(jié)為code的內容;
(4)、 B2 00 02 12 03 B6 00 04?B1 :9個字節(jié)表示的便是code 的內容;
? ? ? 該code[] 包含的實現(xiàn)該方法的JVM 的實際的字節(jié),
?
- 0X B2 :getstatic 指令:表示獲取指定類的靜態(tài)域,并將其值壓入棧頂,后面的0X 00 02 ;查看2號常量池,即將out(sysytem.out)壓入棧
- 0X 12 :ldc:表示將一個常量池壓入操作棧,后面的0X 03 便是這個操作數(shù),查看第3號常量池,為hello world,我們要輸出的內容;
- 0X B6 : invokevirtual,調用實例方法,后面的0X 00 04 ,查看4號常量池 表示java/io/PrintStream的println方法,這個指令彈出兩個操作數(shù),即是調用 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;新行開始時,代碼數(shù)組的偏移量,該偏移量從代碼數(shù)組的起始位置開始;
? ? ? ?3.2)、0X 00 03 表示 line_number=3
?
? ? ? ?3.3)、0X 00 08 表示 start_pc;新行開始時,代碼數(shù)組的偏移量,該偏移量從代碼數(shù)組的起始位置開始;
? ? ? ?3.4)、0X 00 04 表示 line_number=4
LineNumberTable ?中包含了一些調試信息,不做討論;
至此 整個helloworld的class 文件全部分析好了,
另外:
1、文章中提高的指令集參照?http://thinkinmylife.iteye.com/blog/443900
2、對在class 復合數(shù)據(jù)結構中的 9中屬性沒有做詳細的介紹,只是對用到的code_attribute,sourcefile_attribute,linenumbertable_attribute ,有一個簡單的說明,其他還有sythetic、localvariabletable、innerclass、exception、deprecated、constantvalue幾種;
?