1 java 代码

  1. package test;
  2. public class Math {
  3. public int math(byte b, short s, char c, int i, long l, float f, Object o, double d) {
  4. int m1 = 10;
  5. int m2 = 11;
  6. int m3 = (m1 + m2) * 10;
  7. return m3;
  8. }
  9. public static void main(String[] args) {
  10. Math m = new Math();
  11. int res = m.math((byte)1, (short)1, 'a', 1, 1L, 1.0f, new Object(), 1.0 );
  12. System.out.println(res);
  13. }
  14. }

2 编译

  1. C:\Users\HINOC\Desktop\eclipse-workspace\test\src\test> javac Math.java

3 反汇编

  1. C:\Users\HINOC\Desktop\eclipse-workspace\test\src\test> javap -v Math.class > math.txt

4 字节码文件

  1. Classfile /C:/Users/HINOC/Desktop/eclipse-workspace/test/src/test/Math.class
  2. Last modified 2019-5-17; size 543 bytes
  3. MD5 checksum e3d2b2fb7599abe246d20e3aac5de55d
  4. Compiled from "Math.java"
  5. public class test.Math
  6. minor version: 0
  7. major version: 52
  8. flags: ACC_PUBLIC, ACC_SUPER
  9. Constant pool:
  10. #1 = Methodref #4.#18 // java/lang/Object."<init>":()V
  11. #2 = Class #19 // test/Math
  12. #3 = Methodref #2.#18 // test/Math."<init>":()V
  13. #4 = Class #20 // java/lang/Object
  14. #5 = Methodref #2.#21 // test/Math.math:(BSCIJFLjava/lang/Object;D)I
  15. #6 = Fieldref #22.#23 // java/lang/System.out:Ljava/io/PrintStream;
  16. #7 = Methodref #24.#25 // java/io/PrintStream.println:(I)V
  17. #8 = Utf8 <init>
  18. #9 = Utf8 ()V
  19. #10 = Utf8 Code
  20. #11 = Utf8 LineNumberTable
  21. #12 = Utf8 math
  22. #13 = Utf8 (BSCIJFLjava/lang/Object;D)I
  23. #14 = Utf8 main
  24. #15 = Utf8 ([Ljava/lang/String;)V
  25. #16 = Utf8 SourceFile
  26. #17 = Utf8 Math.java
  27. #18 = NameAndType #8:#9 // "<init>":()V
  28. #19 = Utf8 test/Math
  29. #20 = Utf8 java/lang/Object
  30. #21 = NameAndType #12:#13 // math:(BSCIJFLjava/lang/Object;D)I
  31. #22 = Class #26 // java/lang/System
  32. #23 = NameAndType #27:#28 // out:Ljava/io/PrintStream;
  33. #24 = Class #29 // java/io/PrintStream
  34. #25 = NameAndType #30:#31 // println:(I)V
  35. #26 = Utf8 java/lang/System
  36. #27 = Utf8 out
  37. #28 = Utf8 Ljava/io/PrintStream;
  38. #29 = Utf8 java/io/PrintStream
  39. #30 = Utf8 println
  40. #31 = Utf8 (I)V
  41. {
  42. public test.Math();
  43. descriptor: ()V
  44. flags: ACC_PUBLIC
  45. Code:
  46. stack=1, locals=1, args_size=1
  47. 0: aload_0
  48. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  49. 4: return
  50. LineNumberTable:
  51. line 3: 0
  52. public int math(byte, short, char, int, long, float, java.lang.Object, double);
  53. descriptor: (BSCIJFLjava/lang/Object;D)I
  54. flags: ACC_PUBLIC
  55. Code:
  56. stack=2, locals=14, args_size=9
  57. 0: bipush 10
  58. 2: istore 11
  59. 4: bipush 11
  60. 6: istore 12
  61. 8: iload 11
  62. 10: iload 12
  63. 12: iadd
  64. 13: bipush 10
  65. 15: imul
  66. 16: istore 13
  67. 18: iload 13
  68. 20: ireturn
  69. LineNumberTable:
  70. line 6: 0
  71. line 7: 4
  72. line 8: 8
  73. line 9: 18
  74. public static void main(java.lang.String[]);
  75. descriptor: ([Ljava/lang/String;)V
  76. flags: ACC_PUBLIC, ACC_STATIC
  77. Code:
  78. stack=11, locals=3, args_size=1
  79. 0: new #2 // class test/Math
  80. 3: dup
  81. 4: invokespecial #3 // Method "<init>":()V
  82. 7: astore_1
  83. 8: aload_1
  84. 9: iconst_1
  85. 10: iconst_1
  86. 11: bipush 97
  87. 13: iconst_1
  88. 14: lconst_1
  89. 15: fconst_1
  90. 16: new #4 // class java/lang/Object
  91. 19: dup
  92. 20: invokespecial #1 // Method java/lang/Object."<init>":()V
  93. 23: dconst_1
  94. 24: invokevirtual #5 // Method math:(BSCIJFLjava/lang/Object;D)I
  95. 27: istore_2
  96. 28: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
  97. 31: iload_2
  98. 32: invokevirtual #7 // Method java/io/PrintStream.println:(I)V
  99. 35: return
  100. LineNumberTable:
  101. line 13: 0
  102. line 14: 8
  103. line 15: 28
  104. line 16: 35
  105. }
  106. SourceFile: "Math.java"

5 为什么对象被new以后会执行dup操作?

  1. 我们先来看看为dup指令的作用,dup指令可以复制栈顶的一个字再压入栈,也就是把栈顶的内容做个备份。JAVA/CLR是完全基于栈的实现,任何操作都是入栈出栈,没有任何寄存器,所以如果要对某一操作数做两次连续操作,那就要复制两次栈顶操作数,比如:
  1. int x;
  2. int y = x = 20;
  1. 当常数20被压入栈顶后,它要连续两次store到变量xy,所以这里编译后肯定有一个dup操作:
  1. bipush 20
  2. dup
  3. istore_1
  4. istore_2
  1. 如果不做dup操作,那么istore_120存到内存中的x后,再istore_2要么没有操作数,要么是一个其它的操作数。当然这在编译时对连续操作已经做dup操作了,所以不会真的出现这个情况。<br />那么new 指令后,为什么一定要dup操作呢?<br />因为java代码的new操作编译为虚拟机指令后,虚拟机指令new在堆上分配了内存并在栈顶压入了指向这段内存的地址供任何下面的操作来调用,但是在这个操作数被程序员能访问的操作之前,虚拟机自己肯定要调用对象的<br /><init>方法,也就是如果程序员做一个 Type a = new Type();其实要连续两次对栈顶的操作数进行操作。其中一<br />次是虚拟机内部自动调用的,这种情况是99%以上存在的,而java 编译器是一种聪明的编译器,所以只要有new操作<br />就优化为将对象的地址操作数DUP,第一次调用invokespecial <init>时会弹出一个,下面一个留给对该对象访问<br />的操作,即使你的代码是:new Type();没有任何引用。有些虚拟机也会先dup(不同版本编译结果不同),然后<init>时弹出一个操作数,后面会立即pop掉被复制的那个操作数。这样的做目的是为了编译优化。<br />有人说那可以直接从栈顶先store到内存中,需要操作的时候再load到栈顶啊,注意在没有<init>操作之前对象对于程序员是不可见的,否则就会访问到残废的对象,所以只能是先<init>然后才能store到内存中。这两步操作的操作数必须都直接是原来已经存在栈中的,所以只能是dup