JVM--类加载机制--加载时机

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制!

在Java语言里面,类型的加载,连接和初始化过程都是在程序运行期间完成的,这种策略虽然会使类加载时稍微增加一些性能开销,但是会为Java应用程序提供高度的灵活性,Java里天生可以动态扩展的语言特性就是依赖运行期间动态加载和动态连接这个特点实现的。

1.类加载的时机

类从被加载到虚拟机内存开始,到卸载出内存为止,他的整个生命周期包括:加载,验证,准备,解析,初始化,使用,卸载七个阶段。其中的验证,准备,解析三个部分统称为连接。

需要注意的是:加载,验证,准备,初始化,卸载这五个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班的开始,而解析阶段则不一定。(注意是按部就班的开始,而不是按部就班的进行或者完成,因为这些阶段通常都是互相交叉的混合式进行的)

对于初始化阶段,虚拟机严格规定了有且只有5种情况必须立即对类进行初始化。(那么加载,验证,准备自然需要在其之前开始)

①遇到new,getstatic,putstatic,或invokestatic这四条字节码时,如果没有进行过初始化,则需要首先触发其初始化。而对应生成这四条字节码的Java场景是:使用new关键字实例化对象的时候,读取或者设置一个类的静态字段的时候,以及调用一个类的静态方法的时候。需要强调的是:对于静态字段,必须是直接定义的才会被初始化,例如子类继承父类的static并且子类被动引用的时候(直接使用子类的类名,而并没有遇到new关键字),会初始化父类,这时候不会初始化子类。

使用java。lang。reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发器初始化。

③当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。

④当虚拟机启动的时候,用户需要制定一个要执行的主类(包含main方法的类),虚拟机会先初始化这个类。

⑤当时用JDK的动态语言支持的时候,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

下面使用测试代码,测试一个①中强调的内容:

package com.liuxuanjie;

public class Main {
	
	public static void main(String args[])
	{
		System.out.println(Main3.number);
	}
}

class Main2
{
	static 
	{
		System.out.println("父类的静态程序块!");
	}
	public static int number = 541;
}

class Main3 extends Main2
{
	static
	{
		System.out.println("子类的静态程序块!");
	}
}

程序测试结果:

很明显,父类的static区域的内容显示了,表明父类初始化,而子类没有初始化。

该程序涉及到初始化时候的Clinit()隐含方法,参考后续内容。

最后,说明在上述陈述中接口与类的区别:

只是涉及到上述五种情况下的第三种:当一个类初始化的时候,要求其父类全部都已经初始化,但是一个接口在初始化的时候,并不要求其父接口全部完成初始化,只有在真正使用到父接口的时候(例如引用接口中定义的常量)才会初始化。

你可能感兴趣的:(JVM学习)