jvm将类的加载过程分为三个步骤:装载(Load)、链接(Link)和初始化(Initialize)。
类从被加载到虚拟机内存中开始,到卸载出内存,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initiallization)、使用(Using)和卸载(Unloading)这7个阶段。其中验证、准备、解析3个部分统称为连接(Linking),这七个阶段的发生顺序如下图:
图中,加载、验证、准备、初始化、卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段不一定:它在某些情况下可以初始化阶段之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定)。接下来讲解加载、验证、准备、解析、初始化五个步骤,这五个步骤组成了一个完整的类加载过程。使用没什么好说的,卸载属于GC的工作 。
装载是类加载的第一个阶段。有两种时机会触发类装载:
1)预加载
虚拟机启动时装载,装载的是JAVA_HOME/lib/下的rt.jar下的.class文件,这个jar包里面的内容是程序运行时非常常用到的,像java.lang.*、java.util.、java.io. 等等,因此随着虚拟机一起装载。要证明这一点很简单,写一个空的main函数,设置虚拟机参数为"-XX:+TraceClassLoading"来获取类装载信息,运行一下:

2)运行时装载
虚拟机在用到一个A.class文件的时候,会先去内存中查看一下这个A.class文件有没有被装载,如果没有就会按照类的全限定名来装载这个类。

装载阶段做三件事:
1.从zip包中获取,这就是以后jar、ear、war格式的基础
2.从网络中获取,典型应用就是Applet
3.运行时计算生成,典型应用就是动态代理技术
4.由其他文件生成,典型应用就是JSP,即由JSP生成对应的.class文件
5.从数据库中读取,这种场景比较少见
就是将已经读入内存的类的二进制数据合并到JVM运行时环境中去,包含以下步骤:
为什么要做验证?因为class文件未必要从Java源码编译而来,可以使用任何途径产生。
虚拟机如果不检查输入的字节流,对其完全信任的话,很可能会因为载入了有害的字节流而导致系统崩溃。
检验被加载的类是否有正确的内部结构,并和其他类协调一致。
主要包括四种验证:文件格式验证、元数据验证、字节码验证、符号引用验证

符号引用就是一组符号来描述目标,可以是任何字面量。属于编译原理方面的概念如:包括类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。如指向方法区某个类的一个指针。
假设:一个类有一个静态变量,该静态变量是一个自定义的类型,那么经过解析后,该静态变量将是一个指针,指向该类在方法区的内存地址。
例子:
public class TestMain {
private static int i;
private double d;
public static void print() {
}
private boolean trueOrFalse(){
return false;
}
}
这段代码的.class反编译一下:

为类的静态变量赋初值。赋初值两种方式:
注意:只有对类的主动使用才会导致类的初始化。clinit指的是类构造器,主要作用是在类加载过程中的初始化阶段进行执行,执行内容包括静态变量初始化和静态块的执行。
注意事项:
1. 如果类中没有静态变量或静态代码块,那么clinit方法将不会被生成。
2. 在执行clinit方法时,必须先执行父类的clinit方法。
3. clinit方法只执行一次。
4. static变量的赋值操作和静态代码块的合并顺序由源文件中出现的顺序决定。
init指的是实例构造器,主要作用是在类实例化过程中执行,执行内容包括成员变量初始化和代码块的执行。
注意事项:
1. 如果类中没有成员变量和代码块,那么clinit方法将不会被生成。
2. 在执行init方法时,必须先执行父类的init方法。
3. init方法每实例化一次就会执行一次。
4. init方法先为实例变量分配内存空间,再执行赋默认值,然后根据源码中的顺序执行赋初值或代码块。