写点什么

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"
复制代码

参考链接

VM 字节码指令手册 - 查看 Java 字节码

大话+图说:Java字节码指令——只为让你懂

Java字节码(.class文件)格式详解(一)

待看链接

用java字节码解释i++和++i

从字节码的角度分析i++和++i的本质区别


发布于: 2021 年 02 月 23 日阅读数: 23
用户头像

因为只会扯淡,还扯不好淡,所有想扯点什么 2017.11.30 加入

还未添加个人简介

评论

发布
暂无评论
Java训练营第一周习题:01字节码分析