hello world程序执行过程

文章目录

概述

本文分析Windows平台下,一个简单Hello World程序从编译到运行所经历的大致过程,Test.java代码如下:

1
2
3
4
5
public class {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

执行步骤

  1. 命令行执行javac Test.java,编译成.class文件。
  2. 命令行执行java Test,控制台打印结果Hello World!

执行步骤详细分析

一、把.java文件编译成.class

执行javac Test.java命令时,javac编译器将按照《Java虚拟机规范》把Test.java源文件编译成平台无关的Test.class文件,该Class文件的结构可以被虚拟机识别。

二、加载.class文件到JVM

执行java Test命令时,java.exe会装载jvm.dll这个动态链接库,启动虚拟机进程,把Test.class文件加载到虚拟机内存中,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。具体的类加载过程如下:

  1. 加载:应用程序类加载器(Application ClassLoader)获取Test.class的二进制字节流,将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构,并在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
  2. 验证:虚拟机对该Class文件的字节流进行一系列验证(文件格式验证、元数据验证、字节码验证、符号引用验证),确保符合当前虚拟机的要求,不会危害到虚拟机。
  3. 准备:为类变量(被static修饰的变量)分配内存并设置初始值,这些变量所使用的内存都将在方法区中进行分配。
  4. 解析:虚拟机常量池内的符号引用替换为直接引用。
  5. 初始化:根据程序员自定义的内容去初始化类变量和其他资源。

三、JVM执行字节码

使用javap -v Test命令,可以看到main()方法字节码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello World!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 5: 0
line 6: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;

详细字节码指令执行步骤如下:

  1. 执行getstatic指令,把static修饰的System.out的引用存入当前栈帧的操作数栈
  2. 执行ldc指令,把字符串Hello World!的引用从运行时常量池中存入操作数栈
  3. 执行invokevirtual指令,调用PrintStream.println()方法,该指令会使用前两步操作存入操作数栈的值。

参考文档

《深入理解Java虚拟机》
《The Java Virtual Machine Specification》