- 加载
- 验证
- 准备
- 解析
- 初始化
- 类的生命周期由七个部分组成,前五个阶段为类的加载过程;
- 负责从文件系统或者网络系统加载class文件,类加载子系统只负责加载class文件,具体文件是否可以执行则由执行引擎决定
- 通过一个类的全限定名获取定义此类的二进制字节流,通过类加载器,将class文件加载到类装载子系统
- 将这个字节流所代表的静态存储结构转化为方法区(元空间)的运行时数据结构
- 在内存中生成一个代表这个类的java.lang.Class对象(此环节已经生成Class对象),作为方法区这个类的各种数据的访问入口
- 通过类的全限定名获取存储该类的class文件,没有指定从哪里获取
a. 从压缩包中读取,如jar、war
b. 从网络中获取,如Web Applet
c. 动态生成,如动态代理、CGLIB
d. 由其他文件生成,如JSP
e. 从数据库读取
f. 从加密文件中读取
加载时机
- new ,getstatic ,putstatic ,invokestatic
- 反射
- 初始化一个类的子类会去加载其父类
- 启动类(main函数所在的类)
确保Class文件的字节流中包含的信息符合当前虚拟机的要求,保证被加载类的正确性,不危害虚拟机本身,主要包含四种验证 : 文件格式验证,元数据验证,字节码验证,符号引用验证
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
为类变量分配内存,且设置该类变量的初始值,如:初始值零;(不包含final修饰的static,被final修饰的在编译的时候就分配了,准备阶段会为其显示赋值,也不会为实例变量分配初始化,类变量会分配在方法中,而实例变量是会随着对象一起分配到java堆内存中)
- 为静态变量分配内存,赋初始值(实例变量是在创建对象的时候完成赋值)
- 如果被final修饰
○ 编译的时候会给属性添加ConstantValue属性
○ 准备阶段直接完成赋值(不存在赋初始值)
将常量池内的符号引用转换为直接引用过程(建立方法调用虚表),解析动作主要针对类或接口/字段/类方法/接口方法/方法类型等
- 将常量池中的符合引用转为直接引用(可定位到直接地址值)
- 解析后的信息存储到ConstantPoolCache实例中
○ 常量池缓存是为常量池预留的运行时数据结构。保存所有字段访问和调用字节码的解释器运行时信息。缓存是在类被积极使用之前创建和初始化的。每个缓存项在解析时被填充
○ 常量池类ConstantPool中有个属性_cache指向了这个结构
○ 每一条数据对应一个类ConstantPoolCacheEntry
○ - 解析包含
○ 类或接口的解析
○ 字段的解析
○ 方法的解析
○ 接口方法的解析
何时解析
- 加载阶段,解析常量池时
- 用到的时候
- 在执行特定的字节码指令之前进行解析:anewarray、checkcast、getfield、getstatic、instanceof、invokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual、ldc、ldc_w、ldc2_w、multianewarray、new、putfield
初始化阶段就执行类构造器方法() 的过程,此方法不需要用户定义,是javac编译器自动收集类中的所有变量的赋值动作和静态代码块中的语句合并的结果;
多线程下,一个类的初始化时会被同步加锁,以保证只会被加载一次
- 执行静态代码块,完成静态变量的赋值
- 静态字段/静态代码块在字节码层面会生成clinit方法
- 方法中语句的先后顺序与代码的编写顺序息息相关
- 防止并发,初始化阶段底层会加锁



