Java字节码分析

对于源码的效率,但从源码来看有时无法分析出准确的结果,因为不同的编译器版本可能会将相同的源码编译成不同的字节码,Java真正执行的也是字节码,所以要分析源码的性能需要从字节码的角度分析。

查看字节码详细内容 javap

javap

查看classFile的命令并将输出到file.txt

javap -v classFile > file.txt

参数 解释
‐version 版本信息
‐v ‐verbose 输出附加信息
‐l 输出行号和本地变量表
‐public 仅显示公共类和成员
‐protected 显示受保护的/公共类和成员
‐package 显示程序包/受保护的/公共类和成员 (默认)
‐p ‐private 显示所有类和成员
‐c 对代码进行反汇编
‐s 输出内部类型签名
‐sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
‐constants 显示最终常量
‐classpath 指定查找用户类文件的位置
‐cp 指定查找用户类文件的位置
‐bootclasspath 覆盖引导类文件的位置
常量池描述符
Constant Type Value 说明
CONSTANT_Class 7 类或接口的符号引用
CONSTANT_Fieldref 9 字段的符号引用
CONSTANT_Methodref 10 类中方法的符号引用
CONSTANT_InterfaceMethodref 11 接口中方法的符号引用
CONSTANT_String 8 字符串类型常量
CONSTANT_Integer 3 整形常量
CONSTANT_Float 4 浮点型常量
CONSTANT_Long 5 长整型常量
CONSTANT_Double 6 双精度浮点型常量
CONSTANT_NameAndType 12 字段或方法的符号引用
CONSTANT_Utf8 1 UTF-8编码的字符串
CONSTANT_MethodHandle 15 表示方法句柄
CONSTANT_MethodType 16 标志方法类型
CONSTANT_InvokeDynamic 18 表示一个动态方法调用点
字段描述符
FieldType term Type Interpretation
B byte signed byte
C char Unicode character code point in the BasicMultilingual Plane, encoded with UTF-16
D double double-precision floating-point value
F float single-precision floating-point value
I int integer
J long long integer
LClassName; reference an instance of class ClassName
S short signed short
Z boolean true or false
[ reference one array dimension
方法描述符

方法:

Object m(int i, double d, Thread t) {...}

--->描述符

(IDLjava/lang/Thread;)Ljava/lang/Object;

解释:

传入(I 第一个参数int类型 D第二个参数double L第三个参数一个对象,后面是对象的描述java/lang/Thread;)输出Ljava/lang/Object;一个object对象

  1. package JavaCore.JVM.ByteCode;
  2. /*******************************************************************************
  3. * @Copyright (C), 2018-2019,github:Swagger-Ranger
  4. * @FileName: ByteCode_Test
  5. * @Author: liufei32@outlook.com
  6. * @Date: 2019/4/13 14:51
  7. * @Description:
  8. * @Aha-eureka:
  9. *
  10. * javap生成详细的命令
  11. * javap -v ByteCode_Test.class > ByteCode_Test.txt
  12. * 内容:
  13. * 第一部分:显示了生成这个class的java源文件、版本信息、生成时间等。
  14. * 第二部分:显示了该类中所涉及到常量池,共35个常量。
  15. * 第三部分:显示该类的构造器,编译器自动插入的。
  16. * 第四部分:显示了main方的信息。(这个是需要我们重点关注的)
  17. *
  18. * //第一部分
  19. * Classfile /D:/Swagger-Ranger/git-workspace/Algorithms/out/production/Algorithms/JavaCore/JVM/ByteCode/ByteCode_Test.class
  20. * Last modified 2019-4-13; size 617 bytes
  21. * MD5 checksum 646edba623f52c83adb9e067841a1ffb
  22. * Compiled from "ByteCode_Test.java"
  23. * public class JavaCore.JVM.ByteCode.ByteCode_Test
  24. * minor version: 0
  25. * major version: 52
  26. * flags: ACC_PUBLIC, ACC_SUPER
  27. *
  28. * //第二部分
  29. * Constant pool:
  30. *
  31. * //这里在描述常量池时,所有的utf-8类型的都是值,即描述符的内容,而常量描述符的内容则用utf-8对于的常量序号引用来描述,并使用.:
  32. * //等符号来拼接。比如:#1 = Methodref #5.#23 --层层引用还原#5.#23即为-->Class:java/lang/Object."<init>"()V返回void---对没错就是后面注释的内容
  33. *
  34. * //常量的序号和类型 常量描述(使用字段描述符或方法描述符描述) 注释
  35. *
  36. * #1 = Methodref #5.#23 // java/lang/Object."<init>":()V
  37. * #2 = Fieldref #24.#25 // java/lang/System.out:Ljava/io/PrintStream;
  38. * #3 = Methodref #26.#27 // java/io/PrintStream.println:(I)V
  39. * #4 = Class #28 // JavaCore/JVM/ByteCode/ByteCode_Test
  40. * #5 = Class #29 // java/lang/Object
  41. * #6 = Utf8 <init>
  42. * #7 = Utf8 ()V
  43. * #8 = Utf8 Code
  44. * #9 = Utf8 LineNumberTable
  45. * #10 = Utf8 LocalVariableTable
  46. * #11 = Utf8 this
  47. * #12 = Utf8 LJavaCore/JVM/ByteCode/ByteCode_Test;
  48. * #13 = Utf8 main
  49. * #14 = Utf8 ([Ljava/lang/String;)V
  50. * #15 = Utf8 args
  51. * #16 = Utf8 [Ljava/lang/String;
  52. * #17 = Utf8 a
  53. * #18 = Utf8 I
  54. * #19 = Utf8 b
  55. * #20 = Utf8 c
  56. * #21 = Utf8 SourceFile
  57. * #22 = Utf8 ByteCode_Test.java
  58. * #23 = NameAndType #6:#7 // "<init>":()V
  59. * #24 = Class #30 // java/lang/System
  60. * #25 = NameAndType #31:#32 // out:Ljava/io/PrintStream;
  61. * #26 = Class #33 // java/io/PrintStream
  62. * #27 = NameAndType #34:#35 // println:(I)V
  63. * #28 = Utf8 JavaCore/JVM/ByteCode/ByteCode_Test
  64. * #29 = Utf8 java/lang/Object
  65. * #30 = Utf8 java/lang/System
  66. * #31 = Utf8 out
  67. * #32 = Utf8 Ljava/io/PrintStream;
  68. * #33 = Utf8 java/io/PrintStream
  69. * #34 = Utf8 println
  70. * #35 = Utf8 (I)V
  71. *
  72. * //第三部分该类的构造器,编译器自动插入的。
  73. * {
  74. * public JavaCore.JVM.ByteCode.ByteCode_Test();
  75. * descriptor: ()V //构造函数描述,()V-无传入参数并返回空
  76. * flags: ACC_PUBLIC
  77. * Code:
  78. * stack=1, locals=1, args_size=1
  79. * 0: aload_0
  80. * 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  81. * 4: return
  82. * LineNumberTable:
  83. * line 12: 0
  84. * LocalVariableTable:
  85. * Start Length Slot Name Signature
  86. * 0 5 0 this LJavaCore/JVM/ByteCode/ByteCode_Test;
  87. *
  88. * //第四部分 main方的信息。(这个是需要我们重点关注的)
  89. * public static void main(java.lang.String[]);
  90. * descriptor: ([Ljava/lang/String;)V //方法描述,([Ljava/lang/String;)传入一个string一维数组参数;V-返回空
  91. * flags: ACC_PUBLIC, ACC_STATIC //方法修饰符:ACC_PUBLIC :public, ACC_STATIC :static
  92. * Code: //代码块
  93. * stack=2, locals=4, args_size=1 //首先对Code作了统计,stack操作栈(任何操作都先要把值放入操作栈才能操作)有2个,locals本地变量有4个,args_size参数个数有1个
  94. * 0: iconst_2 //将数字2值压入操作栈,位于栈的最上面
  95. * 1: istore_1 //从操作栈中弹出一个元素(数字2),放入到本地变量表中,位于下标为1的位置(下标为0的是this)
  96. * 2: iconst_3 //将数字5值压入操作栈,位于栈的最上面
  97. * 3: istore_2 //从操作栈中弹出一个元素(5),放入到本地变量表中,位于第下标为2个位置
  98. * 4: iload_2 //将本地变量表中下标为2的位置元素压入操作栈(5)
  99. * 5: iload_1 //将本地变量表中下标为1的位置元素压入操作栈(2)
  100. * 6: isub //操作栈中的2个数字相减
  101. * 7: istore_3 // 将相减的结果压入到本地本地变量表中,位于下标为3的位置
  102. * // 开始执行打印语句,那首先要找到打印的内容,通过getstatic #2找到对应的常量即常量池中的#2常量,即可找到对应的引用
  103. * 8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
  104. * 11: iload_3 //将本地变量表中下标为3的位置元素压入操作栈(3)
  105. * // 通过#3号找到对应的常量,然后invokevirtual去执行#3= Methodref 方法引用,即可找到对应的引用,进行方法调用
  106. * 12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
  107. * 15: return //返回
  108. * LineNumberTable: //这里是源码行号和字节码步骤作一一对应,当然因为我将注释复制了过来所以这里源码行号有改变
  109. * line 15: 0
  110. * line 16: 2
  111. * line 17: 4
  112. * line 18: 8
  113. * line 19: 15
  114. * LocalVariableTable: //本地变量表
  115. * 槽位 变量名 字段描述符
  116. * Start Length Slot Name Signature
  117. * 0 16 0 args [Ljava/lang/String;
  118. * 2 14 1 a I
  119. * 4 12 2 b I
  120. * 8 8 3 c I
  121. * }
  122. * SourceFile: "ByteCode_Test.java"
  123. *******************************************************************************/
  124. public class ByteCode_Test {
  125. public static void main( String[] args ) {
  126. int a = 2;
  127. int b = 3;
  128. int c = b - a;
  129. System.out.println(c);
  130. }
  131. }

实例分析

i++与++i

  1. package JavaCore.JVM.ByteCode;
  2. /*******************************************************************************
  3. * @Copyright (C), 2018-2019,github:Swagger-Ranger
  4. * @FileName: ByteCode_iplusplus_plusplusi
  5. * @Author: liufei32@outlook.com
  6. * @Date: 2019/4/14 0:04
  7. * @Description: i++和++i的具体字节码
  8. * @Aha-eureka:
  9. *******************************************************************************/
  10. public class ByteCode_iplusplus_plusplusi {
  11. public void method1() {
  12. int i = 5;
  13. int a = i++;
  14. System.out.println(a);
  15. }
  16. public void method2() {
  17. int i = 5;
  18. int a = ++i;
  19. System.out.println(a);
  20. }
  21. public static void main( String[] args ) {
  22. new ByteCode_iplusplus_plusplusi().method1();
  23. new ByteCode_iplusplus_plusplusi().method2();
  24. }
  25. }
  26. /**
  27. * 命令:javap -v ByteCode_iplusplus_plusplusi.class > ByteCode_iplusplus_plusplusi.txt
  28. *
  29. * Classfile /D:/Swagger-Ranger/git-workspace/Algorithms/out/production/Algorithms/JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi.class
  30. * Last modified 2019-4-14; size 876 bytes
  31. * MD5 checksum 6619df3d2429d5c853b4d1972b1e6504
  32. * Compiled from "ByteCode_iplusplus_plusplusi.java"
  33. * public class JavaCore.JVM.ByteCode.ByteCode_iplusplus_plusplusi
  34. * minor version: 0
  35. * major version: 52
  36. * flags: ACC_PUBLIC, ACC_SUPER
  37. * Constant pool:
  38. * #1 = Methodref #8.#27 // java/lang/Object."<init>":()V
  39. * #2 = Fieldref #28.#29 // java/lang/System.out:Ljava/io/PrintStream;
  40. * #3 = Methodref #30.#31 // java/io/PrintStream.println:(I)V
  41. * #4 = Class #32 // JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi
  42. * #5 = Methodref #4.#27 // JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi."<init>":()V
  43. * #6 = Methodref #4.#33 // JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi.method1:()V
  44. * #7 = Methodref #4.#34 // JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi.method2:()V
  45. * #8 = Class #35 // java/lang/Object
  46. * #9 = Utf8 <init>
  47. * #10 = Utf8 ()V
  48. * #11 = Utf8 Code
  49. * #12 = Utf8 LineNumberTable
  50. * #13 = Utf8 LocalVariableTable
  51. * #14 = Utf8 this
  52. * #15 = Utf8 LJavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi;
  53. * #16 = Utf8 method1
  54. * #17 = Utf8 i
  55. * #18 = Utf8 I
  56. * #19 = Utf8 a
  57. * #20 = Utf8 method2
  58. * #21 = Utf8 main
  59. * #22 = Utf8 ([Ljava/lang/String;)V
  60. * #23 = Utf8 args
  61. * #24 = Utf8 [Ljava/lang/String;
  62. * #25 = Utf8 SourceFile
  63. * #26 = Utf8 ByteCode_iplusplus_plusplusi.java
  64. * #27 = NameAndType #9:#10 // "<init>":()V
  65. * #28 = Class #36 // java/lang/System
  66. * #29 = NameAndType #37:#38 // out:Ljava/io/PrintStream;
  67. * #30 = Class #39 // java/io/PrintStream
  68. * #31 = NameAndType #40:#41 // println:(I)V
  69. * #32 = Utf8 JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi
  70. * #33 = NameAndType #16:#10 // method1:()V
  71. * #34 = NameAndType #20:#10 // method2:()V
  72. * #35 = Utf8 java/lang/Object
  73. * #36 = Utf8 java/lang/System
  74. * #37 = Utf8 out
  75. * #38 = Utf8 Ljava/io/PrintStream;
  76. * #39 = Utf8 java/io/PrintStream
  77. * #40 = Utf8 println
  78. * #41 = Utf8 (I)V
  79. * {
  80. * public JavaCore.JVM.ByteCode.ByteCode_iplusplus_plusplusi();
  81. * descriptor: ()V
  82. * flags: ACC_PUBLIC
  83. * Code:
  84. * stack=1, locals=1, args_size=1
  85. * 0: aload_0
  86. * 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  87. * 4: return
  88. * LineNumberTable:
  89. * line 12: 0
  90. * LocalVariableTable:
  91. * Start Length Slot Name Signature
  92. * 0 5 0 this LJavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi;
  93. *
  94. * public void method1(); //i++
  95. * descriptor: ()V
  96. * flags: ACC_PUBLIC
  97. * Code:
  98. * stack=2, locals=3, args_size=1
  99. * 0: iconst_5 //将5压入操作栈
  100. * 1: istore_1 //从操作栈中弹出变量并保存到下标为1的本地变量表
  101. * 2: iload_1 //加载下标为1的本地变量表中的变量到操作栈
  102. * 3: iinc 1, 1 //将本地变量表中下标为1的变量加1,这句命令iinc直接操作本地变量表并跟了两个参数1,1
  103. * 6: istore_2 //将操作栈中的变量(值为1)弹出并保存到下标为2的本地变量表
  104. * 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
  105. * 10: iload_2 //将本地变量表中下标为2的变量加载到操作栈
  106. * 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V //这里打印传参I就是操作栈中的变量i(值为1)
  107. * 14: return
  108. * LineNumberTable:
  109. * line 15: 0
  110. * line 16: 2
  111. * line 17: 7
  112. * line 18: 14
  113. * LocalVariableTable:
  114. * Start Length Slot Name Signature
  115. * 0 15 0 this LJavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi;
  116. * 2 13 1 i I
  117. * 7 8 2 a I
  118. *
  119. * public void method2(); //++i
  120. * descriptor: ()V
  121. * flags: ACC_PUBLIC
  122. * Code:
  123. * stack=2, locals=3, args_size=1
  124. * 0: iconst_5 //将5压入操作栈
  125. * 1: istore_1 //从操作栈中弹出变量并保存到下标为1的本地变量表
  126. * 2: iinc 1, 1 //将本地变量表中下标为1的变量加1,这句命令iinc直接操作本地变量表并跟了两个参数1,1
  127. * 5: iload_1 //加载下标为1的本地变量表中的变量到操作栈
  128. * 6: istore_2
  129. * 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
  130. * 10: iload_2
  131. * 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
  132. * 14: return
  133. * LineNumberTable:
  134. * line 21: 0
  135. * line 22: 2
  136. * line 23: 7
  137. * line 24: 14
  138. * LocalVariableTable:
  139. * Start Length Slot Name Signature
  140. * 0 15 0 this LJavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi;
  141. * 2 13 1 i I
  142. * 7 8 2 a I
  143. *
  144. * public static void main(java.lang.String[]);
  145. * descriptor: ([Ljava/lang/String;)V
  146. * flags: ACC_PUBLIC, ACC_STATIC
  147. * Code:
  148. * stack=2, locals=1, args_size=1
  149. * 0: new #4 // class JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi
  150. * 3: dup
  151. * 4: invokespecial #5 // Method "<init>":()V
  152. * 7: invokevirtual #6 // Method method1:()V
  153. * 10: new #4 // class JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi
  154. * 13: dup
  155. * 14: invokespecial #5 // Method "<init>":()V
  156. * 17: invokevirtual #7 // Method method2:()V
  157. * 20: return
  158. * LineNumberTable:
  159. * line 27: 0
  160. * line 28: 10
  161. * line 29: 20
  162. * LocalVariableTable:
  163. * Start Length Slot Name Signature
  164. * 0 21 0 args [Ljava/lang/String;
  165. * }
  166. * SourceFile: "ByteCode_iplusplus_plusplusi.java"
  167. */

区别:

  • i++

    只是在本地变量中对数字做了相加,并没有将数据压入到操作栈 将前面拿到的数字1,

    再次从操作栈中拿到,压入到本地变量中
  • ++i

    将本地变量中的数字做了相加,并且将数据压入到操作栈 将操作栈中的数据,

    再次压入到本地变量中

本博客为Swagger-Ranger的笔记分享,文章会持续更新

文中源码地址: https://github.com/Swagger-Ranger

欢迎交流指正,如有侵权请联系作者确认删除: liufei32@outlook.com

Java字节码分析的更多相关文章

  1. JVM 内部原理(七)— Java 字节码基础之二

    JVM 内部原理(七)- Java 字节码基础之二 介绍 版本:Java SE 7 为什么需要了解 Java 字节码? 无论你是一名 Java 开发者.架构师.CxO 还是智能手机的普通用户,Java ...

  2. Java finally语句到底是在return之前还是之后执行(JVM字节码分析及内部体系结构)?

    之前看了一篇关于"Java finally语句到底是在return之前还是之后执行?"这样的博客,看到兴致处,突然博客里的一个测试用例让我产生了疑惑. 测试用例如下: public ...

  3. Java并发编程原理与实战八:产生线程安全性问题原因(javap字节码分析)

    前面我们说到多线程带来的风险,其中一个很重要的就是安全性,因为其重要性因此,放到本章来进行讲解,那么线程安全性问题产生的原因,我们这节将从底层字节码来进行分析. 一.问题引出 先看一段代码 packa ...

  4. 通过字节码分析java中的switch语句

    在一次做题中遇到了switch的问题,由于对switch执行顺序的不了解,在这里简单的通过字节码的方式理解一下switch执行顺序(题目如下): public class Ag{ static pub ...

  5. JVM Java字节码的角度分析switch的实现

    目录 Java字节码的角度分析switch的实现 引子 前置知识 一个妥协而又枯燥的方案 switch的实现 回顾历史 字节码分析 其他实现方式? Java字节码的角度分析switch的实现 作者 k ...

  6. 通过字节码分析Java异常处理机制

    在上一次[https://www.cnblogs.com/webor2006/p/9691523.html]初步对异常表相关的概念进行了了解,先来回顾一下: 其源代码也贴一下: 下面来看一下jclas ...

  7. 透过字节码分析java基本类型数组的内存分配方式。

    我们知道java中new方式创建的对象都是在堆中创建的,而局部变量对应的值存放在栈上.那么java中的int [] arr={1,2,3}是存放在什么地方的呢,int []arr = new int[ ...

  8. JVM-String比较-字节码分析

    一道String字符串比较问题引发的字节码分析 public class a { public static void main(String[] args)throws Exception{ } p ...

  9. Java字节码操纵框架ASM小试

    本文主要内容: ASM是什么 JVM指令 Java字节码文件 ASM编程模型 ASM示例 参考资料汇总 JVM详细指令 ASM是什么 ASM是一个Java字节码操纵框架,它能被用来动态生成类或者增强既 ...

随机推荐

  1. BZOJ2726:任务安排(DP+斜率优化+二分)

    机器上有N个需要处理的任务,它们构成了一个序列.这些任务被标号为1到N,因此序列的排列为1,2,3...N.这N个任务被分成若干批,每批包含相邻的若干任务.从时刻0开始,这些任务被分批加工,第i个任务 ...

  2. Linq 支持动态字查询集合, 也就是说根据传入的值进行查询。

    Linq 支持动态字查询集合, 也就是说根据传入的值进行查询. 比如我们有个类Patient, 其中有个字段PatientName, 现在有Patient集合, 想要查询PatientName为&qu ...

  3. 三台主机搭建LAMP(apache、mariadb、php)

    实验环境:均是CentOS7 httpd:172.16.254.88   2.4.6 PHP:172.16.250.140 5.4.16 mariadb:172.16.250.94 5.5.52 第三 ...

  4. fabnacii数列

    Fibonacci数 时间限制:3000 ms | 内存限制:65535 KB 难度:1 描述 无穷数列1,1,2,3,5,8,13,21,34,55...称为Fibonacci数列,它可以递归地定义 ...

  5. 2.6用tr进行转换

    tr可以对来自标准输入的内容进行字符替换.字符删除以及重复字符压缩.它可以将一组字符变成另一组字符,因而通常也被称为转换命令. 1.tr只能通过stdin(标准输入),而无法通过命令行参数来接受输入. ...

  6. 7.10实习培训日志-markdown Git

    父模块github地址 一. markdown 1. markdown列表 html是一种发布的格式,markdown是一种书写的格式 区块引用 列表 图片 表格 html 标题 记笔记 写博客 2. ...

  7. React Native环境搭建(iOS、Mac)

    http://reactnative.cn/docs/0.42/getting-started.html#content 1.安装Homebrew Homebrew, Mac系统的包管理器,用于安装N ...

  8. 在GridView的RowDataBound事件中获取某行某列的值!

    protected void gdvOrders_RowDataBound(object sender, GridViewRowEventArgs e)   {               if (e ...

  9. lightoj1010【规律】

    思路: 根据案例的规律其实已经猜的差不多了,answer=n*m/2; 有一条边是1的情况,也很好判断,answer=n*m; 就是有一条边是2的时候比较隐秘:是连续2*2一块可以填,然后2*2不填, ...

  10. codeforces986F Oppa Funcan Style Remastered【线性筛+最短路】

    容易看出是用质因数凑n 首先01个因数的情况可以特判,2个的情况就是ap1+bp2=n,b=n/p2(mod p1),这里的b是最小的特解,求出来看bp2<=n则有解,否则无解 然后剩下的情况最 ...