加载
- 主要作用:把编译好的class文件加载到内存中
这个阶段主要是编译器把Java代码编译为class文件后,在启动jvm进程时,jvm会把class文件转为二进制数据存在内存中,然后在方法区创建一个对应Class对象,该对象是访问被加载过后的class对象各种数据的入口。
验证
- 主要作用:验证class(存在内存中的二进制码)是否符合当前jvm版本的规范,已经代码是否符合jvm的规范。
当class文件被加载到内存后,先检验是否符合当前jvm版本规范,因为不同版本的jvm规范是不一致的,新的jvm可能会抛弃或禁用一些指令。
还要检验代码是否符合jvm规范,比如当我们调用一个add(int i)
方法。该方法需要传入一个int
类型的参数,但却传入了String
类型的参数,此时校验不会通过。
准备
- 主要作用:为类变量初始化内存和值。
通过jvm规范验证后,jvm接下来就初始化类变量内存和值,就是以static
修饰的类变量。当类变量以static final
修饰的时候,初始化的值是由用户指定的值。比如:
private static final int i = 10
与
private static int j = 2
当初始化变量i
时,会分配i
的内存以及值10
。
当初始化变量j
时,会分配j
的内存,并把j
的值初始化为零值,也就是0
。并不是用户指定的2
。
原因是:当变量被修饰为final
后,表示该变量不可变,并且不可被继承。也就是变量的内存地址以及值无法被改变,那么用户最初赋予final
修饰的变量的值,也就是最终的值,肯定不会被改变,所以在初始化时,jvm也顺理成章的把final
修饰的变量值初始化为用户指定的值。
解析
- 主要作用:主要针对类、接口、方法、等进行解析。将常量池的符号引用替换为直接引用的过程。也就是将虚拟的引用关系替换为真实的内存引用关系。
符号引用:记录了类相关信息,如方法名,方法参数等,当第一次运行该方法时,会通过符号引用找到方法,然后把符号引用替换为直接引用。
直接引用:直接引用就是内存的偏移量,虚拟机通过偏移量可以直接找到内存上该方法字节码的起始位置。
初始化
- 主要作用:Java代码真正运行的阶段,在这个阶段,jvm会根据代码的执行顺序初始化类对象。
何时触发初始化:
- new对象的时候。当new一个对象的时候,如果该类没有被初始化过,则会初始化该类后。再进行new操作。
- 当读取或设置一个
static
修饰的变量时会进行初始化,有一种情况除外,就是当static
变量以final
修饰时的时候,不会被初始化,因为该常量已经在准备阶段被初始化到了常量池中。还有当调用的方法为static
修饰的时候,调用方法所在类,也会被初始化。 - 当使用反射调用类方法时,调用的类会被初始化。
- 当虚拟机启动后,用户指定了主类。也就是main方法类,该类会被优先初始化。
- 当一个类继承了父类后,它的父类需要先初始化后,才会初始化本类。
使用
当jvm执行完初始化阶段后,jvm就从入口开始执行用户的程序代码。
卸载
当用户代码被jvm执行完毕后,jvm会销毁相关的class对象。最后退出内存。