zoukankan      html  css  js  c++  java
  • Java字节码分析

    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

  • 相关阅读:
    Atitit 引流矩阵与矩阵引流 推广方法 attilax总结
    Atitit 怎么阅读一本书 消化 分析 检索 attilax总结 1. 读书的本质 是数据的处理,大量的数据,处理能力有限的大脑 2 2. ETL数据清洗转换 摘要,缩小数据规模 2 2.1
    Atitit 为什么要读书,读书的好处是什么 attilax总结
    Atititi. naming spec 联系人命名与remark备注指南规范v5 r99.docx
    Atitit 安全规范 指南 常见五种意外防止规范 attilax总结
    数据安全 密码学原理与概论
    Atitit 理财之道分期与利率的比较列表 attilax总结
    Atitit 完整知识体系表 attilax总结 要读哪些书
    Atitit 为什么互联网机会这么大
    Atitit 建立新组织集团模型的框架基本制度与一些原则
  • 原文地址:https://www.cnblogs.com/Swagger-Ranger/p/10707267.html
Copyright © 2011-2022 走看看