类加载过程
类从加载到卸载为止,生命周期包括:加载(Loading)→验证(Preparation)→解析(Resolution)→初始化(Initialization)→使用(Using)→卸载(UnLoading)。
其中验证、准备和解析三个部分称为链接(Linking)。
类加载时机
类加载时机有且只有以下情况:
- 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行初始化,则会进行初始化
- 使用反射机制包中的方法对类进行发射调用的时候,如果类没有进行初始化,则会进行初始化
- 当初始化一个类的时候,若其父类没有被初始化,则先进行父类初始化
- 包含main方法的类会先进行初始化
父类与子类初始化
在类加载时机中的第三点,提到了父类和子类的初始化,这里详细的再介绍一下内部顺序:
- 初始化 父类 静态变量和静态代码块
- 初始化子类的静态变量和静态代码块
- 初始化 父类的实例变量、实例代码块和构造方法
- 初始化子类的实例变量、实例代码块和构造方法
类加载的加载阶段
加载与类加载不同,加载是类加载的过程之一。
加载阶段会生成Class对象,且用户可以使用系统提供的类加载器来完成,也可以由用户自定义的类加载器去完成。这里和类加载器有关,详情戳主页。
加载阶段,JVM完成三件事情:
- 通过类的全限定名来获取此类的二进制字节流
- 将字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在Java堆中生成Class对象,作为访问入口
类加载的链接阶段
链接阶段分为: 验证、准备和解析 三个阶段
验证
验证是链接的第一步,目的是确保Class文件字节流中包含的信息符合虚拟机的要求,不会危害虚拟机自身的安全。
主要验证以下几个方面:
- 文件格式验证:验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理
- 元数据验证:对字节码描述的信息进行语义分析,以确保其信息符合Java语言规范的要求,保证不存在不符合Java语言规范的元数据信息
- 字节码验证:进行数据流和控制流分析,保证被校验类的方法在运行时不会做出危害虚拟机的行为
- 符号引用验证:将符号引用转化为直接引用,可以看作是对类自身以外(常量池中各种符号的引用)的信息进行匹配性的校验。保证解析动作能正常执行。
总结:验证字节码的正确性。
准备
准备阶段是正式为类变量(被static修饰的变量)分配内存并设置变量初始值的阶段,这些内存将在方法区中分配。实例变量分配内存将在初始化时和对象一起分配到堆区。初始值是通常情况下是数据类型的零值。
总结:主要是针对静态变量,进行内存分配和给默认零值。
基本数据类型的零值:
数据类型 | 零值 |
---|---|
int | 0 |
long | 0L |
short | (short)0 |
char | ‘\u0000’ |
byte | (byte)0 |
boolean | false |
float | 0.0f |
double | 0.0d |
reference | null |
解析
解析阶段是虚拟机将常量池内符号引用替换为直接引用的过程。解析主要针对于类或接口、字段、类方法、接口方法四种。
总结:将所有符号引用替换为方法区的原始引用。
类加载的初始化阶段
类初始化阶段是类加载过程的最后一步,到了初始化阶段,才开始真正执行类中定义的Java程序代码(字节码)。此时 静态变量被赋值,静态代码块会被执行。
更多有关Java基础内容,详戳【Java基础】,其他内容,详戳主页。资料参考【深入理解Java虚拟机——周志明版】。