跳过正文
  1. 全部/
  2. 笔记/
  3. 面试/
  4. Java/
  5. JVM/

4. 类加载机制

目录

在Java程序中,对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性。如果一个类被两个不同的加载器加载,即使来源是同一个Class文件,那这也是两个不同的类,主要会体现在,Class对象的equals()方法,isInstance()方法的返回结果,以及使用instance-of关键字做对象所属关系判定等各种情况。## 1. 类的生命周期

Pasted image 20260810001010.png

1. 加载
#

通过一个类的全限定名来获取定义此类的二进制字节流。​

将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。​

在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

2. 验证
#

  • 文件格式验证:例如是否以魔数0xCAFEBABE开头;版本号等能否被虚拟机执行。​
  • 元数据验证:进行语义分析,确保符合Java语言规范。例如这个类不是抽象类,是否实现了其父类或接口之中要求实现的所有方法.​
  • 字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。​
  • 符号引用验证:符号引用验证可以看作是对类自身以外(常量池中的各种符号引用)的各类信息进行匹配性校验 例如:根据符号引用描述的名字能否找到对应的类;或者符号引用中的类、字段、方法的可访问性(private、protected、public、<package>)是否可被当前类访问等。

3. 准备
#

准备阶段主要是为类的静态变量(Static变量)分配内存,并将其初始化为默认值(0,0L,null,false等这种)。

4. 解析
#

解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程。包括(类或接口解析;字段解析;方法解析;接口方法解析)​

符号引用(Symbolic Reference):写在 Class 文件里的"字面量"描述,它不关心内存地址,只描述"我要找谁"(比如类的全限定名、方法名和描述符)。 直接引用(Direct Reference):符号引用在被解析(Resolve)后,指向内存中的实际地址(比如方法区中的内存偏移量、句柄或指针),JVM 拿着它就能直接找到目标。

5. 初始化
#

初始化是为类的静态变量赋予正确的初始值(前面准备阶段是为变量赋零值),JVM负责对类进行初始化,主要对类变量进行初始化。

2. 类加载器和类加载机制
#

在Java程序中,对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性。如果一个类被两个不同的加载器加载,即使来源是同一个Class文件,那这也是两个不同的类,主要会体现在,Class对象的equals()方法,isInstance()方法的返回结果,以及使用instance-of关键字做对象所属关系判定等各种情况。

三层类加载器

  • 启动类加载器:负责加载存放在 <JAVA_HOME>\lib目录,或者被-Xbootclasspath参数所指定的路径中存放的,而且是Java虚拟机能够识别的类库加载到虚拟机的内存中。​

  • 扩展类加载器:这个类加载器是在类sun.misc.Launcher$ExtClassLoader 中以Java代码的形式实现的。它负责加载<JAVA_HOM E>\lib\ext目录中,或者被java.ext.dirs系统变量所 指定的路径中所有的类库​

  • 应用程序类加载器:由于应用程序类加载器是ClassLoader类中的getSystemClassLoader()方法的返回值,所以有些场合中也称它为“系统类加载器”。它负责加载用户类路径 (ClassPath)上所有的类库。

双亲委派模型
#

当一个类加载器收到"加载某个类"的请求时,它不会自己立刻去加载,而是:

  1. 先把这个请求委派给父类加载器去完成。
  2. 如果父类加载器加载成功,就直接返回结果。
  3. 只有父类加载器加载失败(找不到这个类)时,子加载器才会自己尝试加载
Reply by Email