类加载器子系统
类加载器的作用是加载class文件到内存
加载阶段->链接阶段->初始化阶段
-
ClassLoader只负责class文件的加载,至于是否能够运行由执行引擎判断
-
加载的类信息存放在方法区的一个区域,该区域称为常量池,还存放了字符串字面量、数字常量等
类加载过程
判断类是否加载,已经加载了就开始链接
加载阶段
过程
(1)通过全限类名找到类.class文件,获取文件的字节流
(2)将字节流按照静态类的数据格式转换成存储在方法区的运行时数据结构
(3)为该类生成唯一的java.lang.Class的对象,并在堆中分配内存,作为方法调用和属性的接口
找到.class文件->获取字节流->转换成运行时数据结构->内存中生成Class对象
class文件的不同加载方式
(1)从本地文件系统加载
(2)网络加载
(3)压缩包加载,比如jar包、war包
(4)运行时计算生成,动态代理模式下常用
(5)其他文件生成,比如jsp文件
(6)数据库提取
(7)加密文件中获取,比如防反编译的class文件
链接过程
验证(Verify)(检查字节码文件规范)
验证字节流是否符合java虚拟机规范,符合规范就继续链接过程,否则抛出异常
准备(prepare(初始化类变量:分配内存、赋初值为0|null)
为类变量分配内存空间,并赋初值0值(null值)
注意:准备过程只为静态变量赋初值,不会为常量赋初值,因为常量在编译阶段就已经完成了赋值
解析(Resolve)(解析引用)
将符号引用解析成直接引用
符号引用: 用符号来描述引用的目标
直接引用: 直接指向目标的指针、偏移量
初始化过程
初始化
执行构造器方法 cinit
构造器方法
javac编译时会将类的静态变量赋值语句、静态代码块合并成一个构造器方法
(1)构造器方法的指令执行顺序与java代码的语句顺序一致
(2)JVM会保证父类的构造器方法在子类构造器方法之前完成执行
(3)构造器方法只执行静态代码块语句和类变量的赋值,如果源代码中无这两者就不会生成构造器方法
(4)JVM会为构造器方法加线程同步锁,保证每个类只加载一次
例子
private static int num = 1;
static{
num = 2;
number = 20;
}
private static int number=10;
上述特殊情况:number的声明在符值后面,但是jvm依然能初始化,因为在构造器方法之前已经完成了链接阶段的prepare过程,该过程为number符值了0
过程: linking.prepare:number=0->init:number=20, number=10
init阶段的符值是严格按照代码顺序的