在class文件中描述的各种信息最终都需要加载到虚拟机中之后才能被运行和使用。
虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
Java语言里面:类型的加载和连接过程都是在运行期间完成的,这样会在类加载时稍微增加一些内存开销。但是却能为java应运程序提供高度的灵活性。Java中天生可以动态扩展的特性就是运行期间动态加载和动态链接这个特点实现的。
类加载时机:
生命周期:加载Loading 验证Verification 准备Preparation 解析Resolution 初始化 Initialzation 使用 Using 卸载 Unloading 七个阶段。
对于初始化阶段虚拟机规范严格规定了有且只有四种情况必须立即对类进行初始化。
1)遇到 new、getstatic、putstatic、invokestatic 四条字节码指令时。场景:使用new关键字实例化对象的时候,读取或者设置一个类的静态字段、以及调用一个类的静态方法的时候。
2)使用java.lang.reflect包的方法对类进行发射调用的时候。
3)当初始化一个类的时候发现其父类还没有初始化。
4)虚拟机启动时候指定的执行主类。
通过子类引用父类的静态字段,不会导致子类初始化。
通过数组定义来引用类,不能触发此类的初始化。
常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。
一个接口在初始化时候并不要求其父类接口全部都完成了初始化。只有在真正使用到的时候才会初始化。
类加载过程:
加载Loading:
通过一个类的全限定名来获取定义此类的二进制字节流(从ZIP包中、网络中【applet】、运行时计算生成【动态代理】、从数据库中读取)。
将这个字节流代表的静态存储结构转化为方法区的运行时数据结构。
在堆中生成一个代表这个类的java.lang.Class 对象。作为方法区这些数据的访问入口。
验证:
确保Class文件字节流中包含的信息符合当前的虚拟机要求。并且不会危害虚拟机自生安全。
文件格式校验:是否以魔数0xCAFEBABE 开头、主次版本号、常量池中是否有不支持的类型。。。
元数据验证:类是否有父类、这个类的父类是否继承了不被允许的类。。。
字节码验证:进行数据流和控制流分析。
符号引用验证:对类自身以外的信息进行匹配验证。
准备:
为类变量分配内存并设置类变量初始值的阶段。
解析:
初始化:
类加载器:
对于任意一个类都需要由加载她的类加载器和这个类本身一同却类其在虚拟机中的唯一性。