前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试官:解释一下Java字节码文件中的JVM指令

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

作者头像
南风
发布2019-11-22 09:50:53
6890
发布2019-11-22 09:50:53
举报
文章被收录于专栏:Java大联盟Java大联盟

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

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

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

代码语言:javascript
复制
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。

代码语言:javascript
复制
javac HelloWorld.java

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

代码语言:javascript
复制
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 命令完成。

代码语言:javascript
复制
javap -c HelloWorld

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

代码语言:javascript
复制
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."<init>":()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 方法执行完毕。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java大联盟 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档