什么是类加载机制
Java 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这个过程被称作虚拟机的类加载机制。
类加载机制的生命周期
类加载机制的生命周期分为加载、验证、准备、解析、初始化、使用和卸载七个阶段,其中验证、准备和解析统称为连接
-
加载阶段:就是类的数据从 Class 文件加载到内存的过程
加载所需要完成的三件事情:
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将这个二进制字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在堆中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问路口
加载阶段和连接阶段的部分动作是交叉进行的
-
验证阶段:确保 Class 文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身安全。包括文件格式验证、元数据验证、字节码验证以及符号引用验证
-
准备阶段:为类中静态变量分配内存并设置初始值,通常情况为数据的零值,若为 final 类型则不变
-
解析阶段:Java 虚拟机将常量池内符合引用替换为直接引用的过程。符合引用以一组符合来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可
解析可能在初始化之后 -
初始化阶段:为类的静态变量赋予正确的初始值
什么是类加载器
实现 “通过一个类的全限定名来获取描述该类的二进制字节流” 的动作的代码称为 “类加载器” (Class Loader)
类和类加载器
对于任意的一个类,都必须由加载它的类加载器和这个类本身一起共同确定其在Java虚拟机中的唯一确定性,每
一个类加载器,都拥有独立的类名称空间
类加载器的分类
从Java虚拟机角度
-
启动类加载器(Bootstrap ClassLoader):
这个类加载器使用 C++ 语言实现,是虚拟机的一部分
-
其他所有类加载器:
这些类加载器都由 Java 语言实现,独立存在于虚拟机外部,并且全部继承自抽象类 java.lang.ClassLoader
从Java开发人员角度
-
启动类加载器(Bootstrap ClassLoader):
启动类加载器负责加载存放在 JDK\jre\lib 下,或被-Xbootclasspath参数指定的路径的类
-
扩展类加载器(Extension ClassLoader):
扩展类加载器负责加载 JDK\jre\lib\ext 目录中,或者由 java.ext.dirs 系统变量指定的路径中的所有类库(如 javax.* 开头的类)
-
应用类加载器(Application ClassLoader):
应用类加载器负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器。
双亲委派模型
双亲委派模型的工作过程是:如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都是传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围内没有找到所需要的类)时,子加载器才会自己去完成加载
双亲委派模型的好处:Java中类随着它的类加载器一起具备了一种带有优先级的层次关系
例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。因此,如果开发者尝试编写一个与rt.jar类库中重名的Java类,可以正常编译,但是永远无法被加载运行。
参考资料
- 深入理解Java虚拟机,周志明著,机械工业出版社,2020年