类加载的生命周期
- 类加载过程:加载->连接->初始化。
- 连接过程又可分为三步:验证->准备->解析。
总结: 五个阶段:加载、验证、准备、解析、初始化
PS:其中解析阶段在某些情况下可以在初始化阶段之后开始。
PS:这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。
- 加载: 查找并加载类的二进制数据
- 连接
- 验证: 确保被加载的类的正确性
- 准备: 为类的静态变量分配内存,并将其初始化为默认值
- 解析: 把类中的符号引用转换为直接引用
- 初始化:为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。
- 使用: 类访问方法区内的数据结构的接口, 对象是Heap区的数据
- 卸载: 结束生命周期
类加载器的层次
- 启动类加载器: 负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库。启动类加载器是无法被Java程序直接引用的
- 扩展类加载器: 负责加载JDK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
- 应用程序类加载器: 负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器
- 自定义类加载器: 因为JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件,因此如果编写了自己的ClassLoader,便可以做到如下几点:
- 在执行非置信代码之前,自动验证数字签名。
- 动态地创建符合用户特定需要的定制化构建类。
- 从特定的场所取得java class,例如数据库中和网络中。
Class.forName()和ClassLoader.loadClass()区别?
Class.forName()
: 将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块;ClassLoader.loadClass()
: 只将.class文件加载到jvm中,在显式调用newInstance()
时才会去执行static块。Class.forName(name, initialize, loader)
带参函数可控制是否加载static块。
类加载机制
类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载呢?这就需要提到类加载机制了。
- 全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托:先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。(这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效)
- 双亲委派机制(推荐使用): 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
双亲委派模型的好处
双亲委派模型保证了 Java 程序的稳定运行,可以避免类的重复加载(JVM 区分不同类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的是两个不同的类),也保证了 Java 的核心 API 不被篡改。
如果没有使用双亲委派模型,而是每个类加载器加载自己的话就会出现一些问题,比如我们编写一个称为 java.lang.Object
类的话,那么程序运行的时候,系统就会出现两个不同的 Object
类。双亲委派模型可以保证加载的是 JRE 里的那个 Object
类,而不是你写的 Object
类。这是因为在加载你的 Object
类时,会委托给 父加载器去加载,这时发现已经加载过了 Object
类,会直接返回,不会去加载你写的 Object
类。
扩展
JVM 判定两个 Java 类是否相同的具体规则:JVM 不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即使两个类来源于同一个 Class
文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相同。