jvm类加载过程包括5个过程,分别为加载,验证,准备,解析,初始化。
1,加载
①:通过一个类的全限定名来获取定义此类的二进制字节流。
②:将这个字节流代表的静态存储结构转化为方法区的运行时数据结构。
③:在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
2,验证:确保class文件的字节流中包含的信息符合虚拟机的要求,并且不会危害虚拟机自身的安全。分为4个阶段,文件格式验证,元数据验证,字节码验证,符号引用验证。
①:文件格式验证。保证输入的字节流能正确的解析并存储于方法区内,格式上符合描述一个Java类型信息的要求。这阶段的验证是基于二进制字节流进行的,只有通过了这阶段的验证,字节流才会进入内存的方法区中保存,而后面的三个验证阶段都是基于方法区的存储结构。具体验证内容包括但不限于是否以魔数0xCAFEBABE开头,主次版本号是否在当前虚拟机处理范围内,常量池的常量是否存在不被支持的常量类型,常量索引指向是否正确等等。
②:元数据验证。对类的元数据信息进行语义验证,保证不存在不符合Java语言规范的元数据信息。具体验证包括但不限于是否存在父类,是否继承final类,是否实现了所有必须实现的类,重载是否正确等等。
③:字节码验证。通过数据流和控制流确定程序语义是否合法,符合逻辑。
④:符号引用验证。确保解析动作能正常执行。
3,准备:为类变量分配内存和设置类变量初始值。此处类变量即被static修饰的变量,后面的初始化指的是数据类型都初始化为0,其他为null,而被static final修饰的类常量直接初始化为定义值。
4,解析:将常量池中的符号引用转化为直接引用,主要针对类或接口,字段,类方法,接口方法,方法类型,方法句柄,调用点限定符7类符号引用进行解析。
5,初始化:执行类构造器<clinit>()方法的过程。
<clinit>()方法是由编译期自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的,编译期收集的顺序是有语句在源文件中出现的顺序所决定的,静态代码块中只能访问到定义在静态代码块之前的变量,定义在他之后的变量可以赋值,但是不能访问。同时虚拟机会保证子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕,所以虚拟机中第一个被执行的类为object类。接口中不能使用静态代码块,但仍然有变量初始化的操作,因此也有<clinit>()方法,但接口或接口的实现类初始化时不会先去执行父接口或接口的初始化,只当其中的变量使用时才初始化。