美文网首页Java
一个求水仙花数的类的字节码分析

一个求水仙花数的类的字节码分析

作者: 冬天里的懒喵 | 来源:发表于2021-08-06 14:52 被阅读0次

    1.源码

    代码如下:

    package com.dhb.geektimestudy.kimmking.week1;
    
    public class Hello {
    
        private static final int min = 100;
        private static final int max = 1000;
        
        public static void main(String[] args) {
            Hello.findLotus();
        }
        
        public void findLotus() {
            
            for(int i=min;i<max;i++) {
                int first = i/100;
                int second = i/10%10;
                int third = i%10;
                if(first*first*first + second*second*second + third*third*third == i) {
                    System.out.println(i);
                }
            }
        }
    }
    
    

    2.字节码

    使用javap得到字节码:
    javap -verbose com.dhb.geektimestudy.kimmking.week1.Hello

    Classfile /D:/workspace-mashibing/geektime-study/build/classes/java/main/com/dhb/geektimestudy/kimmking/week1/Hello.class
      Last modified 2021-8-5; size 917 bytes
      MD5 checksum 57013feb11612a9db90c9b078976db4f
      Compiled from "Hello.java"
    public class com.dhb.geektimestudy.kimmking.week1.Hello
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #6.#33         // java/lang/Object."<init>":()V
       #2 = Methodref          #3.#34         // com/dhb/geektimestudy/kimmking/week1/Hello.findLotus:()V
       #3 = Class              #35            // com/dhb/geektimestudy/kimmking/week1/Hello
       #4 = Fieldref           #36.#37        // java/lang/System.out:Ljava/io/PrintStream;
       #5 = Methodref          #38.#39        // java/io/PrintStream.println:(I)V
       #6 = Class              #40            // java/lang/Object
       #7 = Utf8               min
       #8 = Utf8               I
       #9 = Utf8               ConstantValue
      #10 = Integer            100
      #11 = Utf8               max
      #12 = Integer            1000
      #13 = Utf8               <init>
      #14 = Utf8               ()V
      #15 = Utf8               Code
      #16 = Utf8               LineNumberTable
      #17 = Utf8               LocalVariableTable
      #18 = Utf8               this
      #19 = Utf8               Lcom/dhb/geektimestudy/kimmking/week1/Hello;
      #20 = Utf8               main
      #21 = Utf8               ([Ljava/lang/String;)V
      #22 = Utf8               args
      #23 = Utf8               [Ljava/lang/String;
      #24 = Utf8               MethodParameters
      #25 = Utf8               findLotus
      #26 = Utf8               first
      #27 = Utf8               second
      #28 = Utf8               third
      #29 = Utf8               i
      #30 = Utf8               StackMapTable
      #31 = Utf8               SourceFile
      #32 = Utf8               Hello.java
      #33 = NameAndType        #13:#14        // "<init>":()V
      #34 = NameAndType        #25:#14        // findLotus:()V
      #35 = Utf8               com/dhb/geektimestudy/kimmking/week1/Hello
      #36 = Class              #41            // java/lang/System
      #37 = NameAndType        #42:#43        // out:Ljava/io/PrintStream;
      #38 = Class              #44            // java/io/PrintStream
      #39 = NameAndType        #45:#46        // println:(I)V
      #40 = Utf8               java/lang/Object
      #41 = Utf8               java/lang/System
      #42 = Utf8               out
      #43 = Utf8               Ljava/io/PrintStream;
      #44 = Utf8               java/io/PrintStream
      #45 = Utf8               println
      #46 = Utf8               (I)V
    {
      public com.dhb.geektimestudy.kimmking.week1.Hello();
        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 3: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/dhb/geektimestudy/kimmking/week1/Hello;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=0, locals=1, args_size=1
             0: invokestatic  #2                  // Method findLotus:()V
             3: return
          LineNumberTable:
            line 9: 0
            line 10: 3
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       4     0  args   [Ljava/lang/String;
        MethodParameters:
          Name                           Flags
          args
    
      public static void findLotus();
        descriptor: ()V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=4, args_size=0
             0: bipush        100
             2: istore_0
             3: iload_0
             4: sipush        1000
             7: if_icmpge     62
            10: iload_0
            11: bipush        100
            13: idiv
            14: istore_1
            15: iload_0
            16: bipush        10
            18: idiv
            19: bipush        10
            21: irem
            22: istore_2
            23: iload_0
            24: bipush        10
            26: irem
            27: istore_3
            28: iload_1
            29: iload_1
            30: imul
            31: iload_1
            32: imul
            33: iload_2
            34: iload_2
            35: imul
            36: iload_2
            37: imul
            38: iadd
            39: iload_3
            40: iload_3
            41: imul
            42: iload_3
            43: imul
            44: iadd
            45: iload_0
            46: if_icmpne     56
            49: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
            52: iload_0
            53: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
            56: iinc          0, 1
            59: goto          3
            62: return
          LineNumberTable:
            line 14: 0
            line 15: 10
            line 16: 15
            line 17: 23
            line 18: 28
            line 19: 49
            line 14: 56
            line 22: 62
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
               15      41     1 first   I
               23      33     2 second   I
               28      28     3 third   I
                3      59     0     i   I
          StackMapTable: number_of_entries = 3
            frame_type = 252 /* append */
              offset_delta = 3
              locals = [ int ]
            frame_type = 52 /* same */
            frame_type = 250 /* chop */
              offset_delta = 5
    }
    SourceFile: "Hello.java"
    

    3.字节码分析

    3.1 常量池及类属性部分

    版本号:

      minor version: 0  //次版本号 0000
      major version: 52 //主版本号 1.8
      flags: ACC_PUBLIC, ACC_SUPER //类访问标识 public
    //常量池内容 从#1开始,#0的常量池有特殊作用
    Constant pool:
       #1 = Methodref          #6.#33         // java/lang/Object."<init>":()V
       #2 = Methodref          #3.#34         // com/dhb/geektimestudy/kimmking/week1/Hello.findLotus:()V
       #3 = Class              #35            // com/dhb/geektimestudy/kimmking/week1/Hello
       #4 = Fieldref           #36.#37        // java/lang/System.out:Ljava/io/PrintStream;
       #5 = Methodref          #38.#39        // java/io/PrintStream.println:(I)V
       #6 = Class              #40            // java/lang/Object
       #7 = Utf8               min
       #8 = Utf8               I
       #9 = Utf8               ConstantValue
      #10 = Integer            100
      #11 = Utf8               max
      #12 = Integer            1000
      #13 = Utf8               <init>
      #14 = Utf8               ()V
      #15 = Utf8               Code
      #16 = Utf8               LineNumberTable
      #17 = Utf8               LocalVariableTable
      #18 = Utf8               this
      #19 = Utf8               Lcom/dhb/geektimestudy/kimmking/week1/Hello;
      #20 = Utf8               main
      #21 = Utf8               ([Ljava/lang/String;)V
      #22 = Utf8               args
      #23 = Utf8               [Ljava/lang/String;
      #24 = Utf8               MethodParameters
      #25 = Utf8               findLotus
      #26 = Utf8               first
      #27 = Utf8               second
      #28 = Utf8               third
      #29 = Utf8               i
      #30 = Utf8               StackMapTable
      #31 = Utf8               SourceFile
      #32 = Utf8               Hello.java
      #33 = NameAndType        #13:#14        // "<init>":()V
      #34 = NameAndType        #25:#14        // findLotus:()V
      #35 = Utf8               com/dhb/geektimestudy/kimmking/week1/Hello
      #36 = Class              #41            // java/lang/System
      #37 = NameAndType        #42:#43        // out:Ljava/io/PrintStream;
      #38 = Class              #44            // java/io/PrintStream
      #39 = NameAndType        #45:#46        // println:(I)V
      #40 = Utf8               java/lang/Object
      #41 = Utf8               java/lang/System
      #42 = Utf8               out
      #43 = Utf8               Ljava/io/PrintStream;
      #44 = Utf8               java/io/PrintStream
      #45 = Utf8               println
      #46 = Utf8               (I)V
    

    3.2 findLotus() 方法

     descriptor: ()V  //主要描述方法的参数和返回值进行描述,改方法没有参数和返回值,因此为()V
     flags: ACC_PUBLIC, ACC_STATIC   //访问标识,说明该方法时 public static修饰
      Code:
          stack=3, locals=4, args_size=0  //stack深度为3,本地变量为4 由于方法没有输入参数,因此args_size=0
    

    关于code中 指令部分,用下表来分析

    指令 程序计数器 局部变量表 stack 说明
    0: bipush 100 0 < >< >< >< > 100 将100压入stack顶
    2: istore_0 2 <100><><><> 将stack顶部的100写入局部变量表中index为0的位置
    3: iload_0 3 <100>< >< >< > 100 将局部变量表#0的数据load到stack顶
    4: sipush 1000 4 <100>< >< >< > 1000->100 将1000压入stack顶部
    7: if_icmpge 62 7 <100>< >< >< > 1000->100 判断stack中的两个数据,栈底数据100是否大于等于栈顶1000,如果满足则跳转到程序计数器为62的指令
    10: iload_0 10 <100>< >< >< > 100 将局部变量表中#0load到stack顶部
    11: bipush 100 11 <100>< >< >< > 100->100 将100压入stack顶部
    13: idiv 13 <100>< >< >< > 1 将stack底部的数据除以stack顶部的数据,得到的结果存在stack顶部
    14: istore_1 14 <100><1>< >< > 将stack顶部的数据写入到局部变量表的#1位置
    15: iload_0 15 <100><1 >< >< > 100 将局部变量表中#0load到stack顶部
    16: bipush 10 16 <100><1>< >< > 10->100 将10压入stack顶部
    18: idiv 18 <100><1 >< >< > 10 将stack底部的数据100除以stack顶部的数据10,得到的结果10存在stack顶部
    19: bipush 10 19 <100><1>< >< > 10->10 将10压入stack顶部
    21: irem 20 <100><1 >< >< > 0 将stack底部的数据10与stack顶部的数据10取余,得到的结果0存在stack顶部
    22: istore_2 22 <100><1><0>< > 将stack顶部数据0存入局部变量表的#2位置
    23: iload_0 23 <100><1><0>< > 100 将局部变量表#0的数据load到stack顶
    24: bipush 10 24 <100><1><0>< > 10->100 将10压入stack顶部
    26: irem 26 <100><1><0>< > 0 将stack底部的数据10与stack顶部的数据10取余,得到的结果0存在stack顶部
    27: istore_3 27 <100><1><0><0> 将stack顶部数据0存入局部变量表的#3位置
    28: iload_1 28 <100><1><0><0> 1 将局部变量表#1的数据load到stack顶
    29: iload_1 29 <100><1><0><0> 1->1 将局部变量表#1的数据再次load到stack顶
    30: imul 30 <100><1><0><0> 1 将stack底部的数据1与stack顶部的数据1求积,得到的结果1存在stack顶部
    31: iload_1 31 <100><1><0><0> 1->1 将局部变量表#1的数据load到stack顶
    32: imul 32 <100><1><0><0> 1 stack底部的数据1与stack顶部的数据1求积,得到的结果1存在stack顶部
    33: iload_2 33 <100><1><0><0> 0-> 1 将局部变量表#2的数据load到stack顶
    34: iload_2 34 <100><1><0><0> 0->0->1 将局部变量表#2的数据load到stack顶
    35: imul 35 <100><1><0><0> 0->1 将stack顶部的两个数据0,0求积,其结果0存在stack顶部
    36: iload_2 36 <100><1><0><0> 0->0->1 将局部变量表#2的数据load到stack顶
    37: imul 37 <100><1><0><0> 0->1 将stack顶部的两个数据0,0求积,其结果0存在stack顶部
    38: iadd 38 <100><1><0><0> 1 将stack顶部两个数据0,1求和,其结果存在stack顶部
    39: iload_3 39 <100><1><0><0> 0->1 将局部变量表#3的数据load到stack顶
    40: iload_3 40 <100><1><0><0> 0->0->1 将局部变量表#3的数据再次load到stack顶
    41: imul 41 <100><1><0><0> 0->1 将stack顶部两个数据0,0求积,其结果0存在stack顶部
    42: iload_3 42 <100><1><0><0> 0->0->1 将局部变量表#3的数据load到stack顶
    43: imul 43 <100><1><0><0> 0->1 将stack顶部两个数据0,0求积,其结果0存在stack顶部
    44: iadd 44 <100><1><0><0> 1 将stack顶部两个数据0,1求和,其结果1存在stack顶部
    45: iload_0 45 <100><1><0><0> 100->1 将局部变量表#0的数据load到stack顶
    46: if_icmpne 56 56 <100><1><0><0> 比较stack顶部两个数据是否相等,如果不等则跳转到56对应的字节码的指令,此出不等,则会发生跳转
    49: getstatic #4 49 <100><1><0><0> 调用静态方法,方法名为常量池的#4位置,即 java/lang/System.out:Ljava/io/PrintStream;
    52: iload_0 52 <100><1><0><0> 局部变量表#0所在的值 将局部变量表#0load
    53: invokevirtual #5 53 <100><1><0><0> 局部变量表#0所在的值 调用实例方法,方法名在常量池的#5,即java/io/PrintStream.println:(I)V
    56: iinc 0, 1 56 <101><1><0><0> 将局部变量表#0的变量增加1 ,变成了101
    59: goto 3 3 <101><1><0><0> 跳转到3对应的字节码
    62: return 62 < >< >< >< > 无返回值,方法结束

    上表中即是方法findLotus的核心计算过程的字节码及对应的程序计数器、局部变量表、stack的执行全过程。
    上述过程中只对所有指令做了一次描述,对于goto之后的过程都省略了。实际上执行的过程则会根据执行的if判断和goto进行跳转。
    剩余部分字节码:

    //行号表
     LineNumberTable:
            line 14: 0
            line 15: 10
            line 16: 15
            line 17: 23
            line 18: 28
            line 19: 49
            line 14: 56
            line 22: 62
     //方法的局部变量描述,也就是代码中定义的局部变量名
     LocalVariableTable:
            Start  Length  Slot  Name   Signature
               15      41     1 first   I
               23      33     2 second   I
               28      28     3 third   I
                3      59     0     i   I
     //stack图的属性 为了提高JVM在类型检查的验证过程的效率
          StackMapTable: number_of_entries = 3
            frame_type = 252 /* append */
              offset_delta = 3
              locals = [ int ]
            frame_type = 52 /* same */
            frame_type = 250 /* chop */
              offset_delta = 5
    

    3.3 构造函数

    在生成的字节码中,可以看到生成了Hello默认的无参构造函数。

      public com.dhb.geektimestudy.kimmking.week1.Hello();
        //无输入参数和返回值
        descriptor: ()V
        //访问标识 public
        flags: ACC_PUBLIC
        Code:
          //stack深度1,局部变量1,args为1
          stack=1, locals=1, args_size=1
             //将局部变量load到stack
             0: aload_0
             //调用Object的init方法
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          //行号表
          LineNumberTable:
            line 3: 0
          //本地变量表
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Lcom/dhb/geektimestudy/kimmking/week1/Hello;
    

    3.4 main方法

      public static void main(java.lang.String[]);
        //main输入参数 string数组
        descriptor: ([Ljava/lang/String;)V
        //访问标识 public static
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          //stack深度为0 局部变量1 
          stack=0, locals=1, args_size=1
             //调用 findLotus 方法
             0: invokestatic  #2                  // Method findLotus:()V
             3: return
          //行号表
          LineNumberTable:
            line 9: 0
            line 10: 3
          //局部变量表
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       4     0  args   [Ljava/lang/String;
        MethodParameters:
          Name                           Flags
          args
    

    相关文章

      网友评论

        本文标题:一个求水仙花数的类的字节码分析

        本文链接:https://www.haomeiwen.com/subject/tyobvltx.html