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对象

package JavaCore.JVM.ByteCode;

/*******************************************************************************
* @Copyright (C), 2018-2019,github:Swagger-Ranger
* @FileName: ByteCode_Test
* @Author: liufei32@outlook.com
* @Date: 2019/4/13 14:51
* @Description:
* @Aha-eureka:
*
* javap生成详细的命令
* javap -v ByteCode_Test.class > ByteCode_Test.txt
* 内容:
* 第一部分:显示了生成这个class的java源文件、版本信息、生成时间等。
* 第二部分:显示了该类中所涉及到常量池,共35个常量。
* 第三部分:显示该类的构造器,编译器自动插入的。
* 第四部分:显示了main方的信息。(这个是需要我们重点关注的)
*
* //第一部分
* Classfile /D:/Swagger-Ranger/git-workspace/Algorithms/out/production/Algorithms/JavaCore/JVM/ByteCode/ByteCode_Test.class
* Last modified 2019-4-13; size 617 bytes
* MD5 checksum 646edba623f52c83adb9e067841a1ffb
* Compiled from "ByteCode_Test.java"
* public class JavaCore.JVM.ByteCode.ByteCode_Test
* minor version: 0
* major version: 52
* flags: ACC_PUBLIC, ACC_SUPER
*
* //第二部分
* Constant pool:
*
* //这里在描述常量池时,所有的utf-8类型的都是值,即描述符的内容,而常量描述符的内容则用utf-8对于的常量序号引用来描述,并使用.:
* //等符号来拼接。比如:#1 = Methodref #5.#23 --层层引用还原#5.#23即为-->Class:java/lang/Object."<init>"()V返回void---对没错就是后面注释的内容
*
* //常量的序号和类型 常量描述(使用字段描述符或方法描述符描述) 注释
*
* #1 = Methodref #5.#23 // java/lang/Object."<init>":()V
* #2 = Fieldref #24.#25 // java/lang/System.out:Ljava/io/PrintStream;
* #3 = Methodref #26.#27 // java/io/PrintStream.println:(I)V
* #4 = Class #28 // JavaCore/JVM/ByteCode/ByteCode_Test
* #5 = Class #29 // java/lang/Object
* #6 = Utf8 <init>
* #7 = Utf8 ()V
* #8 = Utf8 Code
* #9 = Utf8 LineNumberTable
* #10 = Utf8 LocalVariableTable
* #11 = Utf8 this
* #12 = Utf8 LJavaCore/JVM/ByteCode/ByteCode_Test;
* #13 = Utf8 main
* #14 = Utf8 ([Ljava/lang/String;)V
* #15 = Utf8 args
* #16 = Utf8 [Ljava/lang/String;
* #17 = Utf8 a
* #18 = Utf8 I
* #19 = Utf8 b
* #20 = Utf8 c
* #21 = Utf8 SourceFile
* #22 = Utf8 ByteCode_Test.java
* #23 = NameAndType #6:#7 // "<init>":()V
* #24 = Class #30 // java/lang/System
* #25 = NameAndType #31:#32 // out:Ljava/io/PrintStream;
* #26 = Class #33 // java/io/PrintStream
* #27 = NameAndType #34:#35 // println:(I)V
* #28 = Utf8 JavaCore/JVM/ByteCode/ByteCode_Test
* #29 = Utf8 java/lang/Object
* #30 = Utf8 java/lang/System
* #31 = Utf8 out
* #32 = Utf8 Ljava/io/PrintStream;
* #33 = Utf8 java/io/PrintStream
* #34 = Utf8 println
* #35 = Utf8 (I)V
*
* //第三部分该类的构造器,编译器自动插入的。
* {
* public JavaCore.JVM.ByteCode.ByteCode_Test();
* descriptor: ()V //构造函数描述,()V-无传入参数并返回空
* flags: ACC_PUBLIC
* Code:
* stack=1, locals=1, args_size=1
* 0: aload_0
* 1: invokespecial #1 // Method java/lang/Object."<init>":()V
* 4: return
* LineNumberTable:
* line 12: 0
* LocalVariableTable:
* Start Length Slot Name Signature
* 0 5 0 this LJavaCore/JVM/ByteCode/ByteCode_Test;
*
* //第四部分 main方的信息。(这个是需要我们重点关注的)
* public static void main(java.lang.String[]);
* descriptor: ([Ljava/lang/String;)V //方法描述,([Ljava/lang/String;)传入一个string一维数组参数;V-返回空
* flags: ACC_PUBLIC, ACC_STATIC //方法修饰符:ACC_PUBLIC :public, ACC_STATIC :static
* Code: //代码块
* stack=2, locals=4, args_size=1 //首先对Code作了统计,stack操作栈(任何操作都先要把值放入操作栈才能操作)有2个,locals本地变量有4个,args_size参数个数有1个
* 0: iconst_2 //将数字2值压入操作栈,位于栈的最上面
* 1: istore_1 //从操作栈中弹出一个元素(数字2),放入到本地变量表中,位于下标为1的位置(下标为0的是this)
* 2: iconst_3 //将数字5值压入操作栈,位于栈的最上面
* 3: istore_2 //从操作栈中弹出一个元素(5),放入到本地变量表中,位于第下标为2个位置
* 4: iload_2 //将本地变量表中下标为2的位置元素压入操作栈(5)
* 5: iload_1 //将本地变量表中下标为1的位置元素压入操作栈(2)
* 6: isub //操作栈中的2个数字相减
* 7: istore_3 // 将相减的结果压入到本地本地变量表中,位于下标为3的位置
* // 开始执行打印语句,那首先要找到打印的内容,通过getstatic #2找到对应的常量即常量池中的#2常量,即可找到对应的引用
* 8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
* 11: iload_3 //将本地变量表中下标为3的位置元素压入操作栈(3)
* // 通过#3号找到对应的常量,然后invokevirtual去执行#3= Methodref 方法引用,即可找到对应的引用,进行方法调用
* 12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
* 15: return //返回
* LineNumberTable: //这里是源码行号和字节码步骤作一一对应,当然因为我将注释复制了过来所以这里源码行号有改变
* line 15: 0
* line 16: 2
* line 17: 4
* line 18: 8
* line 19: 15
* LocalVariableTable: //本地变量表
* 槽位 变量名 字段描述符
* Start Length Slot Name Signature
* 0 16 0 args [Ljava/lang/String;
* 2 14 1 a I
* 4 12 2 b I
* 8 8 3 c I
* }
* SourceFile: "ByteCode_Test.java"
*******************************************************************************/ public class ByteCode_Test { public static void main( String[] args ) {
int a = 2;
int b = 3;
int c = b - a;
System.out.println(c);
}
}

实例分析

i++与++i

package JavaCore.JVM.ByteCode;

/*******************************************************************************
* @Copyright (C), 2018-2019,github:Swagger-Ranger
* @FileName: ByteCode_iplusplus_plusplusi
* @Author: liufei32@outlook.com
* @Date: 2019/4/14 0:04
* @Description: i++和++i的具体字节码
* @Aha-eureka:
*******************************************************************************/ public class ByteCode_iplusplus_plusplusi { public void method1() {
int i = 5;
int a = i++;
System.out.println(a);
} public void method2() {
int i = 5;
int a = ++i;
System.out.println(a);
} public static void main( String[] args ) {
new ByteCode_iplusplus_plusplusi().method1();
new ByteCode_iplusplus_plusplusi().method2();
}
} /**
* 命令:javap -v ByteCode_iplusplus_plusplusi.class > ByteCode_iplusplus_plusplusi.txt
*
* Classfile /D:/Swagger-Ranger/git-workspace/Algorithms/out/production/Algorithms/JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi.class
* Last modified 2019-4-14; size 876 bytes
* MD5 checksum 6619df3d2429d5c853b4d1972b1e6504
* Compiled from "ByteCode_iplusplus_plusplusi.java"
* public class JavaCore.JVM.ByteCode.ByteCode_iplusplus_plusplusi
* minor version: 0
* major version: 52
* flags: ACC_PUBLIC, ACC_SUPER
* Constant pool:
* #1 = Methodref #8.#27 // java/lang/Object."<init>":()V
* #2 = Fieldref #28.#29 // java/lang/System.out:Ljava/io/PrintStream;
* #3 = Methodref #30.#31 // java/io/PrintStream.println:(I)V
* #4 = Class #32 // JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi
* #5 = Methodref #4.#27 // JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi."<init>":()V
* #6 = Methodref #4.#33 // JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi.method1:()V
* #7 = Methodref #4.#34 // JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi.method2:()V
* #8 = Class #35 // java/lang/Object
* #9 = Utf8 <init>
* #10 = Utf8 ()V
* #11 = Utf8 Code
* #12 = Utf8 LineNumberTable
* #13 = Utf8 LocalVariableTable
* #14 = Utf8 this
* #15 = Utf8 LJavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi;
* #16 = Utf8 method1
* #17 = Utf8 i
* #18 = Utf8 I
* #19 = Utf8 a
* #20 = Utf8 method2
* #21 = Utf8 main
* #22 = Utf8 ([Ljava/lang/String;)V
* #23 = Utf8 args
* #24 = Utf8 [Ljava/lang/String;
* #25 = Utf8 SourceFile
* #26 = Utf8 ByteCode_iplusplus_plusplusi.java
* #27 = NameAndType #9:#10 // "<init>":()V
* #28 = Class #36 // java/lang/System
* #29 = NameAndType #37:#38 // out:Ljava/io/PrintStream;
* #30 = Class #39 // java/io/PrintStream
* #31 = NameAndType #40:#41 // println:(I)V
* #32 = Utf8 JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi
* #33 = NameAndType #16:#10 // method1:()V
* #34 = NameAndType #20:#10 // method2:()V
* #35 = Utf8 java/lang/Object
* #36 = Utf8 java/lang/System
* #37 = Utf8 out
* #38 = Utf8 Ljava/io/PrintStream;
* #39 = Utf8 java/io/PrintStream
* #40 = Utf8 println
* #41 = Utf8 (I)V
* {
* public JavaCore.JVM.ByteCode.ByteCode_iplusplus_plusplusi();
* descriptor: ()V
* flags: ACC_PUBLIC
* Code:
* stack=1, locals=1, args_size=1
* 0: aload_0
* 1: invokespecial #1 // Method java/lang/Object."<init>":()V
* 4: return
* LineNumberTable:
* line 12: 0
* LocalVariableTable:
* Start Length Slot Name Signature
* 0 5 0 this LJavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi;
*
* public void method1(); //i++
* descriptor: ()V
* flags: ACC_PUBLIC
* Code:
* stack=2, locals=3, args_size=1
* 0: iconst_5 //将5压入操作栈
* 1: istore_1 //从操作栈中弹出变量并保存到下标为1的本地变量表
* 2: iload_1 //加载下标为1的本地变量表中的变量到操作栈
* 3: iinc 1, 1 //将本地变量表中下标为1的变量加1,这句命令iinc直接操作本地变量表并跟了两个参数1,1
* 6: istore_2 //将操作栈中的变量(值为1)弹出并保存到下标为2的本地变量表
* 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
* 10: iload_2 //将本地变量表中下标为2的变量加载到操作栈
* 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V //这里打印传参I就是操作栈中的变量i(值为1)
* 14: return
* LineNumberTable:
* line 15: 0
* line 16: 2
* line 17: 7
* line 18: 14
* LocalVariableTable:
* Start Length Slot Name Signature
* 0 15 0 this LJavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi;
* 2 13 1 i I
* 7 8 2 a I
*
* public void method2(); //++i
* descriptor: ()V
* flags: ACC_PUBLIC
* Code:
* stack=2, locals=3, args_size=1
* 0: iconst_5 //将5压入操作栈
* 1: istore_1 //从操作栈中弹出变量并保存到下标为1的本地变量表
* 2: iinc 1, 1 //将本地变量表中下标为1的变量加1,这句命令iinc直接操作本地变量表并跟了两个参数1,1
* 5: iload_1 //加载下标为1的本地变量表中的变量到操作栈
* 6: istore_2
* 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
* 10: iload_2
* 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
* 14: return
* LineNumberTable:
* line 21: 0
* line 22: 2
* line 23: 7
* line 24: 14
* LocalVariableTable:
* Start Length Slot Name Signature
* 0 15 0 this LJavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi;
* 2 13 1 i I
* 7 8 2 a I
*
* public static void main(java.lang.String[]);
* descriptor: ([Ljava/lang/String;)V
* flags: ACC_PUBLIC, ACC_STATIC
* Code:
* stack=2, locals=1, args_size=1
* 0: new #4 // class JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi
* 3: dup
* 4: invokespecial #5 // Method "<init>":()V
* 7: invokevirtual #6 // Method method1:()V
* 10: new #4 // class JavaCore/JVM/ByteCode/ByteCode_iplusplus_plusplusi
* 13: dup
* 14: invokespecial #5 // Method "<init>":()V
* 17: invokevirtual #7 // Method method2:()V
* 20: return
* LineNumberTable:
* line 27: 0
* line 28: 10
* line 29: 20
* LocalVariableTable:
* Start Length Slot Name Signature
* 0 21 0 args [Ljava/lang/String;
* }
* SourceFile: "ByteCode_iplusplus_plusplusi.java"
*/

区别:

  • i++

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

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

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

    再次压入到本地变量中

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

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

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

最新文章

  1. algorithm 学习之 for_each
  2. JavaScript函数柯里化
  3. 利用jquery实现网站中对应栏目下面内容切换效果。
  4. parseInt的源码阅读
  5. Kafka的消息格式
  6. 【学习总结】 init &amp; initWithFrame &amp; initWithCoder
  7. NYOJ 1107 最高的奖励(贪心+优先队列)
  8. eclipse/myeclipse 变量名自动补全问题
  9. Codeforces 161D Distance in Tree
  10. Codeforces Round #366 (Div. 2)_B. Spider Man
  11. C# 委托、匿名方法、lambda简介
  12. 如何修改maven默认仓库(即repository)的路径
  13. 洛谷P3233 [HNOI2014]世界树
  14. Python判断自定义的参数格式是否正确
  15. es6里面的arr方法
  16. IE内核浏览器的404页面问题和IE自动缓存引发的问题
  17. pyenv离线安装python各版本
  18. 获取当前exe的路径
  19. redis 的hash数据类型
  20. QT 运行崩溃:The inferior stopped because it received a signal from the Operating System

热门文章

  1. L3-007 天梯地图(30 分)
  2. 一:AMQP协议标准简单介绍
  3. 【LeetCode】282. Expression Add Operators
  4. 结合Django+celery二次开发定时周期任务
  5. ElementRef, @ViewChild &amp; Renderer
  6. 《精通Spring4.X企业应用开发实战》读后感第五章(Bean作用域)
  7. IDEA上使用github上传代码
  8. Spring入门第十七课
  9. A Beginner&#39;s Guide to HTTP and REST
  10. gideros-with-zerobrane