类的加载器及其加载过程
类加载子系统
作用
- 类加载器子系统负责从文件系统或者网络中加载Class(字节码)文件,class文件在开头有特定的文件标识。
CA FE BA BE
- ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。
- 加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区还会存放运行时常量池信息(Constant pool)
扮演一个快递员的角色,把文件以二进制数据流的方式加载到内存中,成为元数据模板,从而产生对象
类的加载过程
加载 => 链接 => 初始化
加载(Loading)
- 通过一个类的全限定名(包名.类名)获取定义此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
链接(Linking)
分为**验证(Verify)、准备(Prepare)、解析(Resolve)**三个子阶段
验证(Verify)
- 确保Class文件的字节流中国包含信息符合当前虚拟机要求(以CAFEBABE开头)
- 主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
准备(Prepare)
- 为类变量分配内存并且设置该类变量的默认初始值。
- 这里类变量不包括用final修饰的static(finnal修饰后就是常量不再是变量了),因为final在编译时就会分配了,准备阶段会显式初始化,即赋值为确定值
- **同样不会为实例变量(非static)分配初始化,因为类的对象还没创建,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。
解析(Resolve)
- 将常量池内的符号引用(字节码中对其他的类的调用比如Object,System类)转换为直接引用(直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄)的过程
- 事实上,解析操作往往会伴随着JVM在执行完初始化之后再执行
初始化(Initialization)
初始化阶段解释执行类构造器方法clint()的过程
此方法不需要定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。
- 构造器方法中指令按语句在源文件中出现的顺序执行
- 若该类具有父类,JVM会保证子类的clint()执行前,父类的clint()已经执行完毕
- 虚拟机必须保证一个类的clint()方法在多线程下被同步加锁