类加载的时机
1.加载, 验证, 准备, 初始化, 卸载, 这5者的顺序一定
2.在初始化阶段, 虚拟机仅对以下4种情况进行初始化:
- 遇到new, getstatic, putstatic, invokestatic这4条字节码指令时, 对应java代码场景为: new一个对象, 读取或设置一个类的静态字段(被final修饰, 已在编译器把结果放入常量池的静态字段除外)以及调用一个类的静态方法
- 反射调用时, 若没有初始化, 则需初始化
- 当初始化一个类时, 若父类没有初始化, 会先触发其父类的初始化
- 虚拟机启动的主类(main)
注: 一个接口在初始化时, 并不要求其父接口全部都完成了初始化
类加载的过程
加载
完成三个步骤
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在Java堆中生成一个代表这个类的Class对象, 作为方法区这些数据的访问入口
验证
确保Class文件的字节流中包含的信息符合当前虚拟机的要求, 并且不会危害虚拟机自身的安全
大概验证四阶段: 文件格式验证, 元数据验证, 字节码验证和符号引用验证
准备
正式为类变量分配内存并设置类变量初始值, 这些内存将在方法区中分配.
解析
虚拟机将常量池内的符号引用替换为直接引用的过程
- 符号引用: 以一组符号来描述引用的目标, 符号可以是任何形式的字面量, 只要使用时能无歧义的定位到目标即可. 符号引用与虚拟机实现的布局无关, 引用的目标不一定加载到内存中
- 直接引用: 直接引用是直接指向目标的指针, 相对偏移量或者是一个能间接定位到目标的句柄. 直接引用是与虚拟机实现的内存布局相关的, 同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同, 如果有了直接引用, 那引用的目标必定已经在内存中存在
初始化
执行类中定义的Java程序代码
类加载器
类与类加载器
对于任意一个类, 都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性.
即使两个类来源于同一个Class文件, 只要加载他们的类加载器不同, 那这两个类就必定不相等
双亲委派模型
如果一个类加载器收到了类加载的请求, 它首先不会自己去尝试加载这个类, 而是把这个请求委派给父类加载器去完成, 每一个层次的类加载器都是如此, 因此所有的类加载请求最终都应该传递到顶层的启动类加载器中, 只有当父加载器反馈反馈自己无法完成这个请求时, 子加载器才会尝试自己去加载.
这种机制使一个类最终会委派给启动类加载器进行加载, 保证了在程序的各种类加载器环境中都是同一个类