我们知道在运行Java程序时,首先需要把源代码编译成二进制文件也就是class文件,然后虚拟机才能执行。那虚拟机在执行class文件时,都进行了哪些步骤呢。下面我们将详细分享一下。当类也就是class文件被加载到虚拟机内存开始,到卸载出内存为止。它将要执行以下7个步骤:
下面我们看一下每一个阶段的详细流程。
加载也就是把class文件加载到虚拟机内存中。在加载时虚拟机需要完成3个方面的工作:
在加载完成后,虚拟机将这个二进制流存储在虚拟机的方法区中,并且通过在内存中生成的java.lang.Class对象可以访问方法区中的类型数据。
验证的目的是保证加载的class文件中的字节流是符合虚拟机要求的,并且不会威胁虚拟机的安全。在验证阶段虚拟机主要分为4个步骤来验证:
准备阶段的目的是为静态变量分配内存并设置静态变量的默认初始值。并且这个阶段只会为静态变量分配内存并不包括实例变量。实例变量将在对象实例化时随着对象分配在虚拟机的堆中。设置静态变量的默认初始值并不代表是复制操作。例如:
public static int value = 1;
在虚拟机准备阶段时,上述代码中的value并不会设置为1,而是0,因为这时还没有执行Java的赋值运算。不同的基本数据类型的默认初始值是不同的,下面是它们的区别:
正常在准备阶段静态变量的初始化都是默认值,也就是上述列表中的值,但也有例外的可能,如果一个静态变量是final变量,那么在准备阶段时,此变量将会被赋值为指定的值。
public static final int value = 1;
在上述代码中,在准备阶段就会把value设置为1。
解析就是虚拟机将常量池中的符号引用替换成直接引用的过程。在解析时主要分为下面的几种:
初始化阶段主要的目的是为静态变量或者静态代码块执行赋值的操作。静态代码块中的语句只能访问定义在静态代码块之前变量,定义它之后的变量,在静态代码块中可以赋值,但不却不能访问。下面我们看一下具体的代码。
public static Test {
static {
i = 0; // 编译没有问题 虽然静态变量的定义在静态代码码之后但依然可以赋值
System.out.println(i); //编译出错 静态代码块只能访问在它之前定义的静态变量
}
static int i = 1;
}