java 字节码指令_面试官:解释一下Java字节码文件中的JVM指令

Java 之所以流行,一个很重要的原因就是它的跨平台特性,Compile Once, Run Anywhere,编译一次,到处运行。即 Java 源码只需要编译成字节码文件,之后就可以在不同的操作系统(Windows、Mac、Linux)运行,准确讲是运行在操作系统上的 JVM 中。

我们都知道通过命令 javac 来编译 Java 源代码,但是编译的具体流程步骤你有没有深入了解一下呢?相信很多朋友都没有关注过这一块的内容,今天楠哥就带大家一探究竟,梳理一下 Java 的编译过程。

1、创建一个 Java 源文件 HelloWorld.java,并在 main 方法中完成简单的逻辑操作,如下所示。

public class HelloWorld {

public static void main(String[] args) {

int i = 10;

int j = 20;

int k = i+j;

System.out.println(k);

}

}

2、在终端通过 javac 命令编译 HelloWorld.java。

javac HelloWorld.java

3、编译成功之后我们可以看到生成的 16 进制的字节码文件 HelloWorld.class。

cafe babe 0000 0036 001b 0a00 0500 0e09

000f 0010 0a00 1100 1207 0013 0700 1401

0006 3c69 6e69 743e 0100 0328 2956 0100

0443 6f64 6501 000f 4c69 6e65 4e75 6d62

6572 5461 626c 6501 0004 6d61 696e 0100

1628 5b4c 6a61 7661 2f6c 616e 672f 5374

7269 6e67 3b29 5601 000a 536f 7572 6365

4669 6c65 0100 0f48 656c 6c6f 576f 726c

642e 6a61 7661 0c00 0600 0707 0015 0c00

1600 1707 0018 0c00 1900 1a01 001d 636f

6d2f 736f 7574 6877 696e 642f 7465 7374

2f48 656c 6c6f 576f 726c 6401 0010 6a61

7661 2f6c 616e 672f 4f62 6a65 6374 0100

106a 6176 612f 6c61 6e67 2f53 7973 7465

6d01 0003 6f75 7401 0015 4c6a 6176 612f

696f 2f50 7269 6e74 5374 7265 616d 3b01

0013 6a61 7661 2f69 6f2f 5072 696e 7453

7472 6561 6d01 0007 7072 696e 746c 6e01

0004 2849 2956 0021 0004 0005 0000 0000

0002 0001 0006 0007 0001 0008 0000 001d

0001 0001 0000 0005 2ab7 0001 b100 0000

0100 0900 0000 0600 0100 0000 0300 0900

0a00 0b00 0100 0800 0000 3a00 0200 0400

0000 1210 0a3c 1014 3d1b 1c60 3eb2 0002

1db6 0003 b100 0000 0100 0900 0000 1600

0500 0000 0600 0300 0700 0600 0800 0a00

0900 1100 0a00 0100 0c00 0000 0200 0d

4、16 进制的文件我们根本看不出来任何的逻辑结构,所以此时需要对字节码文件进行反汇编,将 16 进制的内容反编译成我们能看懂的 JVM 指令,这里我们使用 javap -c 命令完成。

javap -c HelloWorld

5、反编译之后的 JVM 指令如下所示。

Compiled from "HelloWorld.java"

public class com.southwind.test.HelloWorld {

public com.southwind.test.HelloWorld();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V 4: return

public static void main(java.lang.String[]);

Code:

0: bipush 10

2: istore_1

3: bipush 20

5: istore_2

6: iload_1

7: iload_2

8: iadd

9: istore_3

10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 13: iload_3

14: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 17: return

}

具体解释一下上述的 JVM 指令

第 1 行表示当前的字节码文件编译自 HelloWorld.java。

第 3 行表示调用 HelloWorld 的无参构造函数来实例化当前对象。

第 4 行到第 7 行表示无参构造函数的执行流程。

第 5 行表示把 this 压入操作数栈中。第 6 行表示调用 HelloWorld 父类 Object 的无参构造,我们知道每个对象在实例化的时候都会默认先实例化其父类对象,并且默认调用父类的无参构造。

第 7 行 return 表示构造方法执行完毕。

第 10 行到第 22 行表示 main 方法的执行流程。

第 11 行表示将常量 10 压入操作数栈。

第 12 行表示取出操作数栈栈顶元素,即 10,然后保存到局部变量表第 1 个位置,即变量 i。

第 13 行表示将常量 20 压入操作数栈。

第 14 行表示取出操作数栈栈顶元素,即 20,然后保存到局部变量表第 2 个位置,即变量 j。

第 15 行表示将局部变量表第 1 个变量(i)压入操作数栈。

第 16 行表示将局部变量表第 2 个变量(j)压入操作数栈。

第 17 行表示取出操作数栈中的前两个值相加,并将结果压入操作数栈顶。

第 18 行表示取出操作数栈栈顶元素,保存到局部变量表第 3 个位置,即变量 k。第 19 行表示读取静态实例 PrintStream。

第 20 行表示将局部变量表第 3 个变量(k)压入操作数栈。

第 21 行表示调用 PrintStream 的 println 方法,将操作数栈顶元素(变量 k)输出。

第 22 行 return 表示 main 方法执行完毕。以上就是 Java 源文件的编译之后的 JVM 指令流程,你学会了吗?

来源:微信公众号

作者:楠哥

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值