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: 1Constant 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 加入
还未添加个人简介











评论