# 类加载机制和双亲委派模型

## 一、 类加载机制

JVM将.class文件加载到内存（方法区）中，并对数据进行校验、转换解析和初始化，最终形成被JVM直接使用的Java类型。

类的加载需要分成7个阶段，分别是：加载、验证、准备、解析、初始化、使用和卸载。一般情况下我们只关注前5个阶段。其中验证、准备和解析又统称为连接阶段。

## 二、前5个阶段

**加载**：在这个阶段，虚拟机需要完成以下三个事情：

1. 通过一个类的全限定名来获取定义此类的二进制字节流（这个动作是类加载器完成的）；
2. 将这个字节流所代表的静态存储结构转换成方法区的运行时数据结构；
3. 在内存中生成一个代表这个类的java.lang.Class对象，作为方法区这个类的各种数据的访问入口。

**验证**：验证类数据信息是否符合JVM规范，是否是一个有效的字节码文件。

**准备**：给类静态变量分配内存空间，并初始化（与程序无关，系统初始化）。

**解析**：将常量池中所有的符号引用转成直接引用。

**初始化**：负责将所有static域按照程序指定操作对应执行（给static变量赋值，执行static代码块的内容）。

上述阶段没有严格的先后执行顺序，通常都是交叉执行的。

## 三、类加载器的分类

虚拟机设计团队把类加载阶段中的“通过一个类的权限定名来获取此类的二进制字节流”这个动作放到JVM外部去实现，以便让程序自己决定如何去获取所需要的类，实现这个代码的模块成为“类加载器”。

对于任意一个类，都需要由加载它的类和这个类本身一同确立其在Java虚拟机中的唯一性。

从JVM的角度来讲，只存在两种不同的类加载器，一个是**启动类加载器**，使用C++实现，是虚拟机的一部分；一个就是**其他类加载器**，使用java语言实现，独立于虚拟机外部，并且全部继承自抽象类java.lang.ClassLoader。

从开发人员的角度，类加载器还可以分的更细致一点：

**启动类加载器**

在HotSpot虚拟机中，BootStrap ClassLoader用C++语言编写并嵌入到JVM内部，主要加载`JAVA_HOME/lib`目录下的所有类，或者加载由选项`-Xbootclasspath`指定的路径下的类。

**拓展类加载器**

Extension ClassLoader继承ClassLoader类，由sun.misc.Launcher$ExtClassLoader实现，加载`JAVA_HOME/lib/ext`目录中的所有类库，或者被java.ext.dirs系统变量所指定的路径中的所有类库。

**应用程序加载器**

Application ClassLoader由sun.misc.Launcher$AppClassLoader实现，由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值类型，所以一般也称它为系统类加载器。它负责加载用户类路径（ClassPath）上所指定的类库，如果程序中没有自定义过自己的类加载器，一般情况下这个就是程序中默认的类加载器。

除此之外，还可以定义自己的类加载器。

## 四、双亲委派模型

双亲委派模型 要求除了顶层的启动类加载器，其他加载器都应当有自己的父类加载器， 当一个类加载器收到类加载任务时，立即将任务委派给它的父类加载器执行，直至委派给最高层的启动类加载器为止。只有当父类反馈自己无法完成这个加载请求（它的搜索范围中没有找到所需的类）时，子加载器才会尝试自己去加载。

![](https://3037548586-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnpynfpU0w3FBTJVlOK%2Fsync%2Fce84069c45c4e2ab9d10ca41b68c45d38da43b55.png?generation=1589894634471302\&alt=media)

**作用**：双亲委派模型可以保证全限名执行的类，只被加载一次。并且Java类随着它的类加载器一起具备了一种带有优先级的层次关系。

双亲委派模型不具有强制性约束，是Java设计者推荐的类加载器实现的方式。父子类的实现不是通过继承而是通过组合的方式来复用父类的代码。

双亲委派模型对于保证Java程序的稳定运行很重要。但是它的实现却非常简单，实现代码都集中在`java.lang.ClassLoader`的loadClass()方法中。

## 五、类的使用和卸载阶段

### 5.1 类的使用

类的使用分为直接引用和被动引用，直接引用会触发类的初始化，被动引用则不会。

**直接引用的情况：**

* 通过new关键字实例化对象、读取或设置类的静态变量、调用类的静态方法；
* 通过反射方式执行以上三种行为；
* 初始化子类的时候，会触发父类的初始化；
* 作为程序入口直接运行时（也就是直接调用main方法）。

**被动引用的情况：**

* 引用父类的静态字段，只会引起父类的初始化，不会引起子类的初始化；
* 定义类数组，不会引起类的初始化；
* 引用类的常量，不会引起类的初始化。

### 5.2 类的卸载

在类使用完后，如果满足下面的情况，类就会被卸载：

* 该类的所有实例都已经被回收，也就是java堆中不存在该类的任何实例；
* 加载该类的ClassLoader已经被回收；
* 该类对应的java.lang.Class对象没有任何地方被引用，无法在任何地方通过反射访问该类的方法。

如果以上三个条件都满足，jvm就会在方法区垃圾回收的时候对类进行卸载，类的卸载过程其实就是在方法区中清空类信息，java类的整个生命周期就结束了。

> 记录自《深入理解Java虚拟机》的笔记，供自己以后复习参考
>
> 参考：
>
> <https://www.cnblogs.com/hf-cherish/p/4970267.html>
