1.加载
根据类的全限定名,加载对应目录下的字节码文件,在方法区生成Class对象,提供数据访问的入口。
2.验证
1.检验字节码的格式规范,比如说cafe babe彩蛋。
2.检验语义规范,检查final标记的类是否有子类,final方法是否被子类重写.
3.操作验证
==> 在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证
==>(通常在解析阶段执行,检查是否通过引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等)
3.准备
1.负责为类中static变量分配空间,并初始化(与程序无关,系统初始化)
2.被final修饰的静态变量,会直接赋予原值;
==>2,3表面意思都是常量。
3.类字段的字段属性表中存在ConstantValue属性,则在准备阶段,其值就是ConstantValue的值。
4.解析
1. 将常量池中所有符号引用转换为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。可以认为是一些静态绑定的会被解析,动态绑定则只会在运行时进行解析;
2. 静态绑定包括一些final方法(不可以重写),static方法(只会属于当前类),构造器(不会被重写)
5.类初始化
调用<clinit>,实例对象的时候,调用<init>,两者是有区别的,前者只对静态变量和静态代码块进行初始化操作。
1. 初始化阶段负责将所有static域按照程序指定操作对应执行(赋值static变量,执行static块)。
2. 如果执行的是static代码块,那么在初始化阶段,JVM就会执行static代码块中定义的所有操作。
3. 所有类变量初始化语句和静态代码块都会在编译时被前端编译器放在收集器里头,存放到一个特殊的方法中,这个方法就是方法,即类/接口初始化方法。
==> 该方法的作用就是初始化一个中的变量,使用用户指定的值覆盖之前在准备阶段里设定的初始值。任何invoke之类的字节码都无法调用方法,因为该方法只能在类加载的过程中由JVM调用。
==> 如果父类还没有被初始化,那么优先对父类初始化,但在方法内部不会显示调用父类的方法,由JVM负责保证一个类的方法执行之前,它的父类方法已经被执行。
==> JVM必须确保一个类在初始化的过程中,如果是多线程需要同时初始化它,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。
==>上述阶段通常都是交叉混合允许,没有严格的先后执行顺序;