从Hello Word出发

从Hello Word出发

与学习一门编程语言一样,我们通过使用Java代码编写一个Hello World程序,使用javap[1]工具查看Hello World程序的字节码,并分析每条字节码指令的执行过程入门Java虚拟机字节码指令。

使用Java代码编写的Hello World程序如代码清单3-1所示。

代码清单3-1 Hello World程序

public class HelloWord {
    public static void main(String[] args) {
        System.out.println("Hello Word");
    }
}

使用javap命令输出Hello World程序的字节码如下。

public static void main(java.lang.String[]);
   Code:
      0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      3: ldc           #3                  // String Hello Word
      5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      8: return

getstatic指令的操作码是0xB2,该指令需要一个操作数,该操作数是常量池中某个CONSTANT_Fieldref_info常量的索引。在本例中,该指令表示获取System的out静态字段,该静态字段的类型为java.io.PrintStream。该指令执行完成后,操作数栈顶存放的就是System的out静态字段的引用,如图3.1所示。

图3.1 getstatic指令执行完成

ldc指令的操作码是0x12,该指令也需要一个操作数,值为常量池中的某个CONSTANT_String_info常量的索引。在本例中,其作用是将常量池中的“Hello Word”字符串的引用放入操作数栈顶。该指令执行完后,操作数栈顶存放的就是字符串“Hello Word”的引用,如图3.2所示。

图3.2 ldc指令执行完成

invokevirtual指令的操作码是0xB6,该指令也需要一个操作数,值为常量池中某个CONSTANT_Methodref_info常量的索引。在本例中,它的作用是调用PrintStream对象的println方法。

invokevirtual指令要求将调用目标方法所需要的参数压入栈顶,除静态方法、类初始化方法<clinit>之外,每个类的成员方法以及类的实例初始化方法<init>的第一个参数都是this引用,在java代码中不需要传递,由编译器编译后生成。

在本例中invokevirtual指令执行之前,操作数栈必须存在一个System.out对象的引用,和println方法所需的参数,并且顺序是严格要求的,正是前面getstatic、ldc两条指令执行的结果。invokevirtual指令执行完成后操作栈的变化如图3.3所示。

图3.3 invokevirtual指令执行完成


注释:

[1] javap是JDK提供的命令行工具,是专门用于分析class文件字节码的工具