小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

深入了解Bytecode

 hmtomyang 2012-03-30
二、Bytecode

1,什么是Bytecode
C/C++編譯器把源代碼編譯成匯編代碼,Java編譯器把Java源代碼編譯成字節(jié)碼bytecode。
Java跨平臺其實(shí)就是基于相同的bytecode規(guī)范做不同平臺的虛擬機(jī),我們的Java程序編譯成bytecode后就可以在不同平臺跑了。
.net框架有IL(intermediate language),匯編是C/C++程序的中間表達(dá)方式,而bytecode可以說是Java平臺的中間語言。
了解Java字節(jié)碼知識對debugging、performance tuning以及做一些高級語言擴(kuò)展或框架很有幫助。

2,使用javap生成Bytecode
JDK自帶的javap.exe文件可以反匯編Bytecode,讓我們看個(gè)例子:
Test.java:
Java代碼 復(fù)制代碼 收藏代碼
  1. public class Test {  
  2.   public static void main(String[] args) {  
  3.     int i = 10000;  
  4.     System.out.println("Hello Bytecode! Number = " + i);  
  5.   }  
  6. }  

編譯后的Test.class:
Java代碼 復(fù)制代碼 收藏代碼
  1. 漱壕   1 +  
  2.           
  3.      
  4.     
  5.     
  6.     
  7.      <init> ()V Code LineNumberTable main ([Ljava/lang/String;)V   
  8. SourceFile   Test.java      
  9.     ! " java/lang/StringBuilder Hello Bytecode! Number =   # $  # %  & ' (  ) * Test java/lang/Object java/lang/System out Ljava/io/PrintStream; append -(Ljava/lang/String;)Ljava/lang/StringBuilder; (I)Ljava/lang/StringBuilder; toString ()Ljava/lang/String; java/io/PrintStream println (Ljava/lang/String;)V !   
  10.              
  11.           *                      >     '<  Y                              

使用javap -c Test > Test.bytecode生成的Test.bytecode:
Java代碼 復(fù)制代碼 收藏代碼
  1. Compiled from "Test.java"  
  2. public class Test extends java.lang.Object{  
  3. public Test();  
  4.   Code:  
  5.    0:  aload_0  
  6.    1:  invokespecial  #1//Method java/lang/Object."<init>":()V  
  7.    4:  return  
  8.   
  9. public static void main(java.lang.String[]);  
  10.   Code:  
  11.    0:  sipush  10000  
  12.    3:  istore_1  
  13.    4:  getstatic  #2//Field java/lang/System.out:Ljava/io/PrintStream;  
  14.    7:  new  #3//class java/lang/StringBuilder  
  15.    10:  dup  
  16.    11:  invokespecial  #4//Method java/lang/StringBuilder."<init>":()V  
  17.    14:  ldc  #5//String Hello Bytecode! Number =   
  18.    16:  invokevirtual  #6//Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;  
  19.    19:  iload_1  
  20.    20:  invokevirtual  #7//Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;  
  21.    23:  invokevirtual  #8//Method java/lang/StringBuilder.toString:()Ljava/lang/String;  
  22.    26:  invokevirtual  #9//Method java/io/PrintStream.println:(Ljava/lang/String;)V  
  23.    29:  return  
  24.   
  25. }  

JVM就是一個(gè)基于stack的機(jī)器,每個(gè)thread擁有一個(gè)存儲著一些frames的JVM stack,每次調(diào)用一個(gè)方法時(shí)生成一個(gè)frame。
一個(gè)frame包括一個(gè)local variables數(shù)組(本地變量表),一個(gè)Operand LIFO stack和運(yùn)行時(shí)常量池的一個(gè)引用。

我們來簡單分析一下生成的字節(jié)碼指令:
aload和iload指令的“a”前綴和“i”分別表示對象引用和int類型,其他還有“b”表示byte,“c”表示char,“d”表示double等等
我們這里的aload_0表示將把local variable table中index 0的值push到Operand stack,iload_1類似
invokespecial表示初始化對象,return表示返回
sipush表示把10000這個(gè)int值push到Operand stack
getstatic表示取靜態(tài)域
invokevirtual表示調(diào)用一些實(shí)例方法
這些指令又稱為opcode,Java一直以來只有約202個(gè)Opcode,具體請參考Java Bytecode規(guī)范。

我們看到Test.class文件不全是二進(jìn)制的指令,有些是我們可以識別的字符,這是因?yàn)橛行┌?、類名和常量字符串沒有編譯成二進(jìn)制Bytecode指令。

3,體驗(yàn)字節(jié)碼增強(qiáng)的魔力
我們J2EE常用的Hibernate、Spring都用到了動態(tài)字節(jié)碼修改來改變類的行為。
讓我們通過看看ASM的org.objectweb.asm.MethodWriter類的部分方法來理解ASM是如何修改字節(jié)碼的:
Java代碼 復(fù)制代碼 收藏代碼
  1. class MethodWriter implements MethodVisitor {  
  2.   
  3.     private ByteVector code = new ByteVector();  
  4.   
  5.     public void visitIntInsn(final int opcode, final int operand) {  
  6.         // Label currentBlock = this.currentBlock;  
  7.         if (currentBlock != null) {  
  8.             if (compute == FRAMES) {  
  9.                 currentBlock.frame.execute(opcode, operand, nullnull);  
  10.             } else if (opcode != Opcodes.NEWARRAY) {  
  11.                 // updates current and max stack sizes only for NEWARRAY  
  12.                 // (stack size variation = 0 for BIPUSH or SIPUSH)  
  13.                 int size = stackSize + 1;  
  14.                 if (size > maxStackSize) {  
  15.                     maxStackSize = size;  
  16.                 }  
  17.                 stackSize = size;  
  18.             }  
  19.         }  
  20.         // adds the instruction to the bytecode of the method  
  21.         if (opcode == Opcodes.SIPUSH) {  
  22.             code.put12(opcode, operand);  
  23.         } else { // BIPUSH or NEWARRAY  
  24.             code.put11(opcode, operand);  
  25.         }  
  26.     }  
  27.   
  28.     public void visitMethodInsn(  
  29.         final int opcode,  
  30.         final String owner,  
  31.         final String name,  
  32.         final String desc)  
  33.     {  
  34.         boolean itf = opcode == Opcodes.INVOKEINTERFACE;  
  35.         Item i = cw.newMethodItem(owner, name, desc, itf);  
  36.         int argSize = i.intVal;  
  37.         // Label currentBlock = this.currentBlock;  
  38.         if (currentBlock != null) {  
  39.             if (compute == FRAMES) {  
  40.                 currentBlock.frame.execute(opcode, 0, cw, i);  
  41.             } else {  
  42.                 /* 
  43.                  * computes the stack size variation. In order not to recompute 
  44.                  * several times this variation for the same Item, we use the 
  45.                  * intVal field of this item to store this variation, once it 
  46.                  * has been computed. More precisely this intVal field stores 
  47.                  * the sizes of the arguments and of the return value 
  48.                  * corresponding to desc. 
  49.                  */  
  50.                 if (argSize == 0) {  
  51.                     // the above sizes have not been computed yet,  
  52.                     // so we compute them...  
  53.                     argSize = getArgumentsAndReturnSizes(desc);  
  54.                     // ... and we save them in order  
  55.                     // not to recompute them in the future  
  56.                     i.intVal = argSize;  
  57.                 }  
  58.                 int size;  
  59.                 if (opcode == Opcodes.INVOKESTATIC) {  
  60.                     size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;  
  61.                 } else {  
  62.                     size = stackSize - (argSize >> 2) + (argSize & 0x03);  
  63.                 }  
  64.                 // updates current and max stack sizes  
  65.                 if (size > maxStackSize) {  
  66.                     maxStackSize = size;  
  67.                 }  
  68.                 stackSize = size;  
  69.             }  
  70.         }  
  71.         // adds the instruction to the bytecode of the method  
  72.         if (itf) {  
  73.             if (argSize == 0) {  
  74.                 argSize = getArgumentsAndReturnSizes(desc);  
  75.                 i.intVal = argSize;  
  76.             }  
  77.             code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 20);  
  78.         } else {  
  79.             code.put12(opcode, i.index);  
  80.         }  
  81.     }  
  82. }  

通過注釋我們可以大概理解visitIntInsn和visitMethodInsn方法的意思。
比如visitIntInsn先計(jì)算stack的size,然后根據(jù)opcode來判斷是SIPUSH指令還是BIPUSH or NEWARRAY指令,并相應(yīng)的調(diào)用字節(jié)碼修改相關(guān)的方法。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多