Java 训练营第一周习题:01 字节码分析
发布于: 2021 年 02 月 23 日
题目
自己写一个简单的 Hello.java,里面需要涉及基本类型,四则运行,if 和 for,然后自己分析一下对应的字节码。
程序源码
编译:javac -g ByteCodeAnalyze.java
public class ByteCodeAnalyze {
public static void main(String[] args) {
ByteCodeAnalyze byteCodeAnalyze = new ByteCodeAnalyze();
for (int i = 0; i < 4; i++) {
if (i == 0) {
System.out.println(byteCodeAnalyze.add(1d, 2d));
} else if (i == 1) {
System.out.println(byteCodeAnalyze.sub(2d, 1d));
} else if (i == 2) {
System.out.println(byteCodeAnalyze.mul(1d, 2d));
} else {
System.out.println(byteCodeAnalyze.div(100d, 3d));
}
}
}
private double add(double a, double b) {
return a + b;
}
private double sub(double a, double b) {
return a - b;
}
private double mul(double a, double b) {
return a * b;
}
private double div(double a, double b) {
return a / b;
}
}
复制代码
程序字节码
用 javap 工具来获取 class 文件中的指令清单。
javap 是标准 JDK 内置的一款工具, 专门用于反编译 class 文件。
查看 class 文件字节码:javap -c -verbose ByteCodeAnalyze
Classfile /Users/zhanghongang/Study/GeekBang/JavaCourse/JAVA-Homework/Week_01/ByteCodeAnalyze.class // class文件的完成路径
Last modified 2021年2月21日; size 1164 bytes // class文件修改时间以及大小
SHA-256 checksum 96f40cd5b0ed04c47715231a8e071c7e3fcf30747cb0fa10e161f18200351f79 // SHA-256 校验和
Compiled from "ByteCodeAnalyze.java" // 从哪个文件编译而来
public class ByteCodeAnalyze
minor version: 0
major version: 59 // java主版本 major_version.minor_version 组成我们的版本号59.0
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #7 // ByteCodeAnalyze
super_class: #2 // java/lang/Object
interfaces: 0, fields: 0, methods: 6, attributes: 1
Constant pool: // 常量池
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Class #8 // ByteCodeAnalyze
#8 = Utf8 ByteCodeAnalyze
#9 = Methodref #7.#3
#10 = Fieldref #11.#12 // java/lang/System.out:Ljava/io/PrintStream;
#11 = Class #13 // java/lang/System
#12 = NameAndType #14:#15 // out:Ljava/io/PrintStream;
#13 = Utf8 java/lang/System
#14 = Utf8 out
#15 = Utf8 Ljava/io/PrintStream;
#16 = Double 2.0d
#18 = Methodref #7.#19 // ByteCodeAnalyze.add:(DD)D
#19 = NameAndType #20:#21 // add:(DD)D
#20 = Utf8 add
#21 = Utf8 (DD)D
#22 = Methodref #23.#24 // java/io/PrintStream.println:(D)V
#23 = Class #25 // java/io/PrintStream
#24 = NameAndType #26:#27 // println:(D)V
#25 = Utf8 java/io/PrintStream
#26 = Utf8 println
#27 = Utf8 (D)V
#28 = Methodref #7.#29 // ByteCodeAnalyze.sub:(DD)D
#29 = NameAndType #30:#21 // sub:(DD)D
#30 = Utf8 sub
#31 = Methodref #7.#32 // ByteCodeAnalyze.mul:(DD)D
#32 = NameAndType #33:#21 // mul:(DD)D
#33 = Utf8 mul
#34 = Double 100.0d
#36 = Double 3.0d
#38 = Methodref #7.#39 // ByteCodeAnalyze.div:(DD)D
#39 = NameAndType #40:#21 // div:(DD)D
#40 = Utf8 div
#41 = Utf8 Code
#42 = Utf8 LineNumberTable
#43 = Utf8 LocalVariableTable
#44 = Utf8 this
#45 = Utf8 LByteCodeAnalyze;
#46 = Utf8 main
#47 = Utf8 ([Ljava/lang/String;)V
#48 = Utf8 i
#49 = Utf8 I
#50 = Utf8 args
#51 = Utf8 [Ljava/lang/String;
#52 = Utf8 byteCodeAnalyze
#53 = Utf8 StackMapTable
#54 = Utf8 a
#55 = Utf8 D
#56 = Utf8 b
#57 = Utf8 SourceFile
#58 = Utf8 ByteCodeAnalyze.java
{
public ByteCodeAnalyze(); // ByteCodeAnalyze类的构造方法
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1 // 栈深最大1,局部变量1,args_size入参是1(如果是实体方法会把this也算入参)
0: aload_0 // aload_0中的0是局部变量表里的Slot 0,意思是将局部变量表里的Slot 0的东西
// 压入操作数栈,这个Slot 0里的东西Name是this,也就是ByteCodeAnalyze的实例
1: invokespecial #1 // 调用父类构造方法 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 2: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LByteCodeAnalyze;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=6, locals=3, args_size=1
0: new #7 // 创建常量池#2位置的类ByteCodeAnalyze的实例,并将此实例引用入栈,栈深度为1
3: dup // 复制栈顶的刚刚放入的引用,再次压栈,这时栈里有两个重复的内容,栈深度为2
4: invokespecial #9 // 弹出栈顶ByteCodeAnalyze引用对象并调用<init>方法,即构造方法,栈深度为1
7: astore_1 // 将栈顶引用变量(ByteCodeAnalyze引用对象)存入本地变量表 Slot 1 位置,栈深度为1
8: iconst_0 // 将int类型数值0推送至栈顶,栈深度为2
9: istore_2 // 将栈顶 int 型数值 0 存入本地变量表 Slot 2 位置,即存入代码中i变量的初始值0
10: iload_2 // 将本地变量表 Slot 2 位置的 0 推送至栈顶,栈深度为3
11: iconst_4 // 将int类型数值 4 推送至栈顶,栈深度为4
12: if_icmpge 102 // 取出并比较栈顶两 int 型数值大小,0 不大于等于 4继续执行,否则,跳转到 102 return,栈深度为1
15: iload_2 // 将本地变量表 Slot 2 位置的 0 推送至栈顶,栈深度为2
16: ifne 36 // 当栈顶 int 型数值不等于 0 时跳转到 36,等于0时,则继续执行
// 19:getstatic,获取指定类的静态域,并将其值压入栈顶,栈深度为3
19: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
22: aload_1 // 将本地变量表 Slot 1 位置的引用变量(ByteCodeAnalyze引用对象)入栈,栈深度为4
23: dconst_1 // 将 double 类型数值 1.0d 推送至栈顶,栈深度为5
// 24: ldc2_w,将 long 或 double 型常量值从常量池中推送至栈顶,2.0d入栈,栈深度6
24: ldc2_w #16 // double 2.0d
// 27,调用实例方法,即调用常量池#18的 ByteCodeAnalyze.add 方法,栈顶的 2.0 和 1.0 分别出栈,
// 作为 ByteCodeAnalyze.add 方法的入参,获取到的结果3.0(1.0+2.0)入栈,栈深度为4(5-2+1)
27: invokevirtual #18 // Method add:(DD)D
// 30,调用 PrintStream.println 方法输入栈顶的 3.0
30: invokevirtual #22 // Method java/io/PrintStream.println:(D)V
33: goto 96 // 无条件跳转到 96,即i++,i由0变为1
36: iload_2
37: iconst_1
38: if_icmpne 58
41: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
44: aload_1
45: ldc2_w #16 // double 2.0d
48: dconst_1
49: invokevirtual #28 // Method sub:(DD)D
52: invokevirtual #22 // Method java/io/PrintStream.println:(D)V
55: goto 96
58: iload_2
59: iconst_2
60: if_icmpne 80
63: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
66: aload_1
67: dconst_1
68: ldc2_w #16 // double 2.0d
71: invokevirtual #31 // Method mul:(DD)D
74: invokevirtual #22 // Method java/io/PrintStream.println:(D)V
77: goto 96
80: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
83: aload_1
84: ldc2_w #34 // double 100.0d
87: ldc2_w #36 // double 3.0d
90: invokevirtual #38 // Method div:(DD)D
93: invokevirtual #22 // Method java/io/PrintStream.println:(D)V
96: iinc 2, 1 // 将局部变量表的 Slot 2位置的变量 +1,即 i++
99: goto 10 // 无条件跳转到 10,即 for 循环的条件判断
102: return
LineNumberTable:
line 4: 0
line 6: 8
line 7: 15
line 8: 19
line 9: 36
line 10: 41
line 11: 58
line 12: 63
line 14: 80
line 6: 96
line 17: 102
LocalVariableTable:
Start Length Slot Name Signature
10 92 2 i I
0 103 0 args [Ljava/lang/String;
8 95 1 byteCodeAnalyze LByteCodeAnalyze;
StackMapTable: number_of_entries = 6
frame_type = 253 /* append */
offset_delta = 10
locals = [ class ByteCodeAnalyze, int ]
frame_type = 25 /* same */
frame_type = 21 /* same */
frame_type = 21 /* same */
frame_type = 15 /* same */
frame_type = 250 /* chop */
offset_delta = 5
}
SourceFile: "ByteCodeAnalyze.java"
复制代码
参考链接
待看链接
划线
评论
复制
发布于: 2021 年 02 月 23 日阅读数: 23
版权声明: 本文为 InfoQ 作者【现实中游走】的原创文章。
原文链接:【http://xie.infoq.cn/article/8569163f1e0d1ec07a8c3af56】。文章转载请联系作者。
现实中游走
关注
因为只会扯淡,还扯不好淡,所有想扯点什么 2017.11.30 加入
还未添加个人简介
评论