您尚未登录,请登录后浏览更多内容! 登录 | 加入最MC

QQ登录

只需一步,快速开始

 找回密码
 加入最MC

QQ登录

只需一步,快速开始

查看: 1526|回复: 0
打印 上一主题 下一主题

Java Bytecode

[复制链接]
  • TA的每日心情
    开心
    2017-9-3 16:08
  • 签到天数: 286 天

    [LV.8]以坛为家I

    跳转到指定楼层
    楼主
    发表于 2017-8-18 00:13:37 | 只看该作者 |0人打赏回帖奖励 |倒序浏览 |阅读模式
    1,什么是Bytecode
    C/C++编译器把源代码编译成汇编代码,Java编译器把Java源代码编译成字节码bytecode。
    Java跨平台其实就是基于相同的bytecode规范做不同平台的虚拟机,我们的Java程序编译成bytecode后就可以在不同平台跑了。
    .net框架有IL(intermediate language),汇编是C/C++程序的中间表达方式,而bytecode可以说是Java平台的中间语言。
    了解Java字节码知识对debugging、performance tuning以及做一些高级语言扩展或框架很有帮助。

    2,使用javap生成Bytecode
    JDK自带的javap.exe文件可以反汇编Bytecode,让我们看个例子:
    Test.java:
    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:
    1. 漱壕 1 +

    2. ()V Code LineNumberTable main ([Ljava/lang/String;)V
    3. SourceFile Test.java
    4. ! " 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 !

    5. * > ' < Y
    复制代码
    使用javap -c Test > Test.bytecode生成的Test.bytecode:
    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."":()V
    7. 4: return

    8. public static void main(java.lang.String[]);
    9. Code:
    10. 0: sipush 10000
    11. 3: istore_1
    12. 4: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
    13. 7: new #3; //class java/lang/StringBuilder
    14. 10: dup
    15. 11: invokespecial #4; //Method java/lang/StringBuilder."":()V
    16. 14: ldc #5; //String Hello Bytecode! Number =
    17. 16: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    18. 19: iload_1
    19. 20: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
    20. 23: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    21. 26: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
    22. 29: return

    23. }
    复制代码
    JVM就是一个基于stack的机器,每个thread拥有一个存储着一些frames的JVM stack,每次调用一个方法时生成一个frame。
    一个frame包括一个local variables数组(本地变量表),一个Operand LIFO stack和运行时常量池的一个引用。

    我们来简单分析一下生成的字节码指令:
    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这个int值push到Operand stack
    getstatic表示取静态域
    invokevirtual表示调用一些实例方法
    这些指令又称为opcode,Java一直以来只有约202個Opcode,具体请参考java Bytecode规范。

    我们看到Test.class文件不全是二进制的指令,有些是我们可以识别的字符,这是因为有些包名、类名和常量字符串没有编译成二进制Bytecode指令。

    3,体验字节码增强的魔力
    我们J2EE常用的hibernate、spring都用到了动态字节码修改来改变类的行为。
    让我们通过看看ASM的org.objectweb.asm.MethodWriter类的部分方法来理解ASM是如何修改字节码的:
    1. class MethodWriter implements MethodVisitor {

    2. private ByteVector code = new ByteVector();

    3. public void visitIntInsn(final int opcode, final int operand) {
    4. // Label currentBlock = this.currentBlock;
    5. if (currentBlock != null) {
    6. if (compute == FRAMES) {
    7. currentBlock.frame.execute(opcode, operand, null, null);
    8. } else if (opcode != Opcodes.NEWARRAY) {
    9. // updates current and max stack sizes only for NEWARRAY
    10. // (stack size variation = 0 for BIPUSH or SIPUSH)
    11. int size = stackSize + 1;
    12. if (size > maxStackSize) {
    13. maxStackSize = size;
    14. }
    15. stackSize = size;
    16. }
    17. }
    18. // adds the instruction to the bytecode of the method
    19. if (opcode == Opcodes.SIPUSH) {
    20. code.put12(opcode, operand);
    21. } else { // BIPUSH or NEWARRAY
    22. code.put11(opcode, operand);
    23. }
    24. }

    25. public void visitMethodInsn(
    26. final int opcode,
    27. final String owner,
    28. final String name,
    29. final String desc)
    30. {
    31. boolean itf = opcode == Opcodes.INVOKEINTERFACE;
    32. Item i = cw.newMethodItem(owner, name, desc, itf);
    33. int argSize = i.intVal;
    34. // Label currentBlock = this.currentBlock;
    35. if (currentBlock != null) {
    36. if (compute == FRAMES) {
    37. currentBlock.frame.execute(opcode, 0, cw, i);
    38. } else {
    39. /*
    40. * computes the stack size variation. In order not to recompute
    41. * several times this variation for the same Item, we use the
    42. * intVal field of this item to store this variation, once it
    43. * has been computed. More precisely this intVal field stores
    44. * the sizes of the arguments and of the return value
    45. * corresponding to desc.
    46. */
    47. if (argSize == 0) {
    48. // the above sizes have not been computed yet,
    49. // so we compute them...
    50. argSize = getArgumentsAndReturnSizes(desc);
    51. // ... and we save them in order
    52. // not to recompute them in the future
    53. i.intVal = argSize;
    54. }
    55. int size;
    56. if (opcode == Opcodes.INVOKESTATIC) {
    57. size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
    58. } else {
    59. size = stackSize - (argSize >> 2) + (argSize & 0x03);
    60. }
    61. // updates current and max stack sizes
    62. if (size > maxStackSize) {
    63. maxStackSize = size;
    64. }
    65. stackSize = size;
    66. }
    67. }
    68. // adds the instruction to the bytecode of the method
    69. if (itf) {
    70. if (argSize == 0) {
    71. argSize = getArgumentsAndReturnSizes(desc);
    72. i.intVal = argSize;
    73. }
    74. code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
    75. } else {
    76. code.put12(opcode, i.index);
    77. }
    78. }
    79. }
    复制代码
    通过注释我们可以大概理解visitIntInsn和visitMethodInsn方法的意思。
    比如visitIntInsn先计算stack的size,然后根据opcode来判断是SIPUSH指令还是BIPUSH or NEWARRAY指令,并相应的调用字节码修改相关的方法。

    评分

    参与人数 1人气 +2 MC币 +13 收起 理由
    NFC_Yan + 2 + 13 唉哟不错哟!

    查看全部评分

    打赏

    0

    收藏

    0

    支持

    0

    反对

    0

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    您需要登录后才可以回帖 登录 | 加入最MC

    本版积分规则