类加载运行机制
类加载形如把.class文件,加载到内存中,得到类对象这样的过程
类加载一共分为五个步骤:
- 加载
- 验证
- 准备
- 解析
- 初始化
加载
类加载的第一步是将编译好的 Java 类的字节码文件加载到 Java 虚拟机(JVM)中。类加载器会根据类的名称找到对应的字节码文件,并将其读取到内存中。
验证
在验证阶段,虚拟机会对加载的字节码文件进行合法性验证,包括文件格式的验证、验证字节码的正确性和安全性等。这一步主要用于确保 class 文件不会引起虚拟机的错误或安全问题。
准备
在准备阶段,虚拟机会为加载的类分配内存空间,并设置类的默认初始值。这些初始值包括零值(0、false或null)或者用户所设置的初始值。
解析
解析阶段是将常量池中的符号引用转换成直接引用的过程。符号引用包括类、接口、字段和方法等的引用。解析操作的主要目的是为了创建对应的直接引用,以便于后续的内存空间分配、类的初始化和方法的调用等操作。
在 Java 语言中,当我们在代码中引用一个类、接口、字段或方法时,实际上是通过符号引用来表示的。符号引用是一种符号化的描述方式,它并不指向具体的内存地址或偏移量,而是通过符号的形式来描述所引用的目标。
举个例子,假设有以下代码片段:
public class ClassA { public void methodA() { ClassB b = new ClassB(); b.methodB(); } } public class ClassB { public void methodB() { System.out.println("Method B is called"); } }
在ClassA
的methodA
方法中,我们创建了一个ClassB
的实例,并调用了它的methodB
方法。
在解析阶段,虚拟机会进行如下的符号引用转换为直接引用的过程:
- 虚拟机会解析
ClassA
对ClassB
的符号引用,找到对应的类ClassB
的描述符和其他信息。 - 虚拟机会在内存中为
ClassB
分配空间,创建ClassB
的实例对象。 - 虚拟机会将
b
变量与实际的内存地址关联起来,这样可以通过b
来访问ClassB
对象。 - 虚拟机会通过
b.methodB()
来调用ClassB
的methodB
方法,这里的methodB
是一个直接引用。
所以,在解析阶段,虚拟机将符号引用转换为直接引用,这样就能够通过直接引用来访问和使用目标类的字段或方法。通过这种方式,虚拟机可以动态地解析和链接类之间的关系,实现类的动态绑定和调用。
初识化
在初始化阶段,虚拟机会执行类的初始化代码,例如静态变量的赋值和静态代码块的执行等。
这一阶段的触发条件包括:
- 类的实例被创建;
- 类的静态方法被调用;
- 类中的静态字段被赋值。
类的初始化是类加载的最后一个步骤。
双亲委派模型
双亲委派模型(又称为双亲委派机制)是Java类加载机制中的一种设计思想和实现方式。
类加载机制是Java虚拟机(JVM)加载类的过程,而双亲委派模型是指在类加载过程中,JVM通过委派的方式来从不同的类加载器去加载类。这个机制主要用于确保类的一致性、安全性和避免重复加载。
在JVM中,有三个类加载器:
- BootStrap ClassLoader 负责加载java标志库中的类
- Extension ClassLoader 负责加载一些非标准的但是是Sun/Oracle 扩展的库的类
- Application ClassLpader 负责加载项目中自己写的类以及第三方库中的一些类
一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最 终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无 法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
双亲委派模型基于以下几个原则:
- 父类加载器优先:如果一个类需要被加载,JVM首先会把这个任务委派给父类加载器来完成。
- 双亲委派:父类加载器在接收到加载请求后,会先检查自己是否已经加载了这个类。如果已经加载,则直接返回该类的Class对象。如果没有加载,则将加载请求向上委派给父类的父类加载器,以此类推,直到顶层的启动类加载器。只有当父类加载器不能完成加载任务时,子加载器才会尝试自己去加载。
3.** 缓存机制:如果一个类被某个加载器加载成功后,这个加载器会将加载结果缓存起来,下次再加载同样的类时直接返回缓存的结果**。
通过双亲委派模型,可以有效地避免类的重复加载和类的不一致性问题。