- 类从加载到卸载,整个生命周期统共分为7个阶段:
- 其中,加载->验证->准备->解析->初始化,这5个过程就是类的加载过程,而且验证->准备->初始化这三个可以统称为连接过程;
- 解析阶段有时候会在初始化之后再开始;
那么,什么时候会触发类的加载?
- 1⃣️ main()所在的主类;
- 2⃣️ 在遇到new、要对某个类的静态变量进行get或set、或者调用它的静态方法时;
- 3⃣️ 对某个类使用反射时;
- 4⃣️ 已经在对某个类进行加载,并且进行到初始化阶段时,而它的父类未加载时,JVM会先进行父类的加载,父类中也是一样的机制,层层递进,直到Object;
- 5⃣️ 加载某个接口并进行到初始化时,且这个接口中使用了父接口(如引用父接口中定义的常量)时,触发对父接口的检查加载;
一、 加载
- JVM根据类的全限定名获取类的二进制字节流;
- 将其中静态数据结构转换成JVM内存中方法区中的数据结构;
- 创建class对象,作为方法区中变量和方法的入口;
注: 这里不一定非要从一个Class文件中获取,既可以从ZIP(jar包)包中获取,也可以
二、 验证
目的:验证Class文件的字节流是否符合JVM的要求,且不会危害到JVM自身;
具体动作:
- 验证该字节流是否符合Class文件格式规范;
- 验证数据类型和方法是否符合Java语言规范;
- 验证符号引用是否都能找到对应的类;
三、准备
- 给静态变量在方法区内分配空间,并赋上初始值;
- 给非静态成员变量与在堆内分配空间,并赋初始值;
比如: public static int v=800;
准备阶段只会给v分配空间,并令v=0;
注: 1.整型赋0,浮点型赋0.0,char型赋’\u0000’,String赋null,boolean赋false
四、解析
- 将常量池中的字面量和符号引用解析,换成直接引用;
符号引用是class文件中的:
- CONSTANT_Class_info
- CONSTANT_Field_info
- CONSTANT_Method_info
- 符号引用:引用的目标不一定要已经加载到内存中;不同布局的虚拟机之间的符号引用必须是一致的,因为它们的字面量形式明确定义在了JVM规范的Class文件格式中;
- 直接引用,是指向目标的指针,相对偏移量是一个能够间接定位到目标的句柄;直接引用代表着引用的目标一定已在内存中存在;
五、初始化
- 是类加载过程的最后一步
- 其实就是执行类构造器方法的过程,它是编译器从类中收集静态变量赋值操作和静态代码块的语句合并而成;
- JVM会保证子类在执行<client>之前,父类的<client>已执行;
-
引申:双亲委派机制
- 类加载器
启动类加载器(负责加载JAVA_HOME目录下类)、拓展类加载器(负责加载JAVA_HOME\lib\ext目录下类)、应用程序类加载器(负责加载用户路径classpath下类库)、自定义加载器(用户自定义类的加载器)
- 流程:当一个类收到类加载请求,他首先不会尝试自己去加载这个 类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都会被传送到启动类加载器中,只有当父类加载器反馈自己在它的加载路径中没有找到所需加载的class时,才会向下反馈让子类加载器尝试加载;
- 优点:这样能够保证类不会被重复加载——已经被父类加载过的类,子类不会再去加载;
- 类加载器