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

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 1000< >< >< >< >100将100压入stack顶
2: istore_02<100><><><>将stack顶部的100写入局部变量表中index为0的位置
3: iload_03<100>< >< >< >100将局部变量表#0的数据load到stack顶
4: sipush 10004<100>< >< >< >1000->100将1000压入stack顶部
7: if_icmpge 627<100>< >< >< >1000->100判断stack中的两个数据,栈底数据100是否大于等于栈顶1000,如果满足则跳转到程序计数器为62的指令
10: iload_010<100>< >< >< >100将局部变量表中#0load到stack顶部
11: bipush 10011<100>< >< >< >100->100将100压入stack顶部
13: idiv13<100>< >< >< >1将stack底部的数据除以stack顶部的数据,得到的结果存在stack顶部
14: istore_114<100><1>< >< >将stack顶部的数据写入到局部变量表的#1位置
15: iload_015<100><1 >< >< >100将局部变量表中#0load到stack顶部
16: bipush 1016<100><1>< >< >10->100将10压入stack顶部
18: idiv18<100><1 >< >< >10将stack底部的数据100除以stack顶部的数据10,得到的结果10存在stack顶部
19: bipush 1019<100><1>< >< >10->10将10压入stack顶部
21: irem20<100><1 >< >< >0将stack底部的数据10与stack顶部的数据10取余,得到的结果0存在stack顶部
22: istore_222<100><1><0>< >将stack顶部数据0存入局部变量表的#2位置
23: iload_023<100><1><0>< >100将局部变量表#0的数据load到stack顶
24: bipush 1024<100><1><0>< >10->100将10压入stack顶部
26: irem26<100><1><0>< >0将stack底部的数据10与stack顶部的数据10取余,得到的结果0存在stack顶部
27: istore_327<100><1><0><0>将stack顶部数据0存入局部变量表的#3位置
28: iload_128<100><1><0><0>1将局部变量表#1的数据load到stack顶
29: iload_129<100><1><0><0>1->1将局部变量表#1的数据再次load到stack顶
30: imul30<100><1><0><0>1将stack底部的数据1与stack顶部的数据1求积,得到的结果1存在stack顶部
31: iload_131<100><1><0><0>1->1将局部变量表#1的数据load到stack顶
32: imul32<100><1><0><0>1stack底部的数据1与stack顶部的数据1求积,得到的结果1存在stack顶部
33: iload_233<100><1><0><0>0-> 1将局部变量表#2的数据load到stack顶
34: iload_234<100><1><0><0>0->0->1将局部变量表#2的数据load到stack顶
35: imul35<100><1><0><0>0->1将stack顶部的两个数据0,0求积,其结果0存在stack顶部
36: iload_236<100><1><0><0>0->0->1将局部变量表#2的数据load到stack顶
37: imul37<100><1><0><0>0->1将stack顶部的两个数据0,0求积,其结果0存在stack顶部
38: iadd38<100><1><0><0>1将stack顶部两个数据0,1求和,其结果存在stack顶部
39: iload_339<100><1><0><0>0->1将局部变量表#3的数据load到stack顶
40: iload_340<100><1><0><0>0->0->1将局部变量表#3的数据再次load到stack顶
41: imul41<100><1><0><0>0->1将stack顶部两个数据0,0求积,其结果0存在stack顶部
42: iload_342<100><1><0><0>0->0->1将局部变量表#3的数据load到stack顶
43: imul43<100><1><0><0>0->1将stack顶部两个数据0,0求积,其结果0存在stack顶部
44: iadd44<100><1><0><0>1将stack顶部两个数据0,1求和,其结果1存在stack顶部
45: iload_045<100><1><0><0>100->1将局部变量表#0的数据load到stack顶
46: if_icmpne 5656<100><1><0><0>比较stack顶部两个数据是否相等,如果不等则跳转到56对应的字节码的指令,此出不等,则会发生跳转
49: getstatic #449<100><1><0><0>调用静态方法,方法名为常量池的#4位置,即 java/lang/System.out:Ljava/io/PrintStream;
52: iload_052<100><1><0><0>局部变量表#0所在的值将局部变量表#0load
53: invokevirtual #553<100><1><0><0>局部变量表#0所在的值调用实例方法,方法名在常量池的#5,即java/io/PrintStream.println:(I)V
56: iinc 0, 156<101><1><0><0>将局部变量表#0的变量增加1 ,变成了101
59: goto 33<101><1><0><0>跳转到3对应的字节码
62: return62< >< >< >< >无返回值,方法结束

上表中即是方法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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值