类从被加载到 JVM 中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。
类加载的过程:加载 -> 链接 -> 初始化
加载 : 简单的说,类加载阶段就是由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到 JVM 内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的对象实例
加载需要借助类加载器,在 Java 虚拟机中,类加载器使用了双亲委派模型,即接收到加载请求时,会先将请求转发给父类加载器。
双亲委派模型:每当一个类加载器接收到加载请求时,它会先将请求转发给父类加载器。在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载
链接 : 链接阶段要做的是将加载到 JVM 中的二进制字节流的类数据信息合并到 JVM 的运行时状态中, 经由验证、准备和解析三个阶段 。
即将类合并至 Java 虚拟机中,使之能够执行的过程。
(1)验证
验证类数据信息是否符合 JVM 规范,是否是一个有效的字节码文件,验证内容涵盖了类数据信息的格式验证、语义分析、操作验证等。格式验证:验证是否符合 class 文件规范
语义验证:检查一个被标记为 final 的类型是否包含子类;检查一个类中的 final 方法视频被子类进行重写;确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同)操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,检查是否通过富豪引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等)(2)准备
为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量不在此操作范围内)被 final 修饰的静态变量,会直接赋予原值;类字段的字段属性表中存在 ConstantValue 属性,则在准备阶段,其值就是 ConstantValue 的值(3)解析
将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。可以认为是一些静态绑定的会被解析,动态绑定则只会在运行是进行解析;静态绑定包括一些 final 方法(不可以重写),static 方法(只会属于当前类),构造器(不会被重写)
初始化 : 是为标记为常量值的字段赋值,以及执行 < clinit > 方法的过程。
只有当初始化完成之后,类才正式成为可执行的状态。以盖房子为例,只有当房子装修过后,Tony 才能真正地住进去。
类的初始化仅会被执行一次,这个特性被用来实现单例的延迟初始化。
那么,类的初始化何时会被触发呢?JVM 规范枚举了下述多种触发情况:
1. 当虚拟机启动时,初始化用户指定的主类;2. 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类;3. 当遇到调用静态方法的指令时,初始化该静态方法所在的类;4. 当遇到访问静态字段的指令时,初始化该静态字段所在的类;5. 子类的初始化会触发父类的初始化;6. 如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化;7. 使用反射 API 对某个类进行反射调用时,初始化这个类;8. 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。