[Java] JVM 在执行 main 方法前的行为
JVM 执行一个 Java 程序时,先从某个指定的 Java 类的 main 方法开始执行代码,同时,传一个字符串数组作为 main 方法的参数。例如在 Unix 系统上,执行下面的命令
java Test reboot Bob Dot Endzo
JVM 会调用 Test 的 main 方法,同时把四个字符串 "reboot", "Bob", "Dot" "Endzo" 作为参数传给 main 方法。
那么,在执行 main 方法前,JVM 需要做什么事情呢?本文将简单地回答这个问题。
JVM 启动调用 main 方法,一共经过四个阶段:加载( Load )、链接( Linkage )、初始化( Initialization )、调用( Invocation )
加载一个类 Test
在最开始,JVM 里面是没有 Test 类的,准确地说,是没有 Test 类的二进制表代码,需要先加载 Test 。JVM 使用类加载器( class loader ) 去搜索 Test 的二进制代码,然后加载。如果类加载器搜索不到,则抛出异常。
链接一个类 Test
在 JVM 里面包含了 Test 类的二进制代码后,需要通过链接动作,将 Test 的二进制形式连接到 JVM 的运行时状态,然后才可以执行 Test 二进制代码。链接包含三个动作:验证( Verify ),准备( Prepare ),解析(Resolve),其中解析行为是可选的。
验证:检查加载完的 Test 类二进制代码格式是否正确,语义是否符合 Java 语言规范、JVM 规范。
准备:分配静态存储空间,以及任何 JVM 所需要的数据结构,例如方法列表( method table )
解析:检查 Test 类引用的类或接口,加载所引用的其他类或接口,并检查他们是否正确。
初始化一个类 Test
初始化包括静态变量的初始化,和静态代码块的初始化。初始化是按文本顺序进行。如果 Test 类可以被初始化,其直接父类需要先被初始化了,而如果直接父类可以被初始化,直接父类的直接父类需要先被初始化了,以此递推。Object 类没有父类,所以 Object 类初始化后,递推结束。
在初始化直接父类的时候,如果直接父类还没有被加载,则需要先加载、链接、初始化直接父类。所以,在初始化一个类阶段,有可能会抛出加载错误、链接错误、初始化错误。
调用 Test.main
当前面的加载、链接、初始化都正常结束后,JVM 会调用 Test 的 main 方法。被调用的 main 必须被修饰为 public , static, void 。可接受的形式有两种:
public staitc void mian(String[] args)
public staitc void main(String... args)
参考资料
12.1 Java Virtual Machine Startup, The Java Language Specification, Java SE 8 Edition