Java

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

2021-08-06  本文已影响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
上一篇下一篇

猜你喜欢

热点阅读