JVM类加载机制

本文详细介绍Java类加载过程,包括加载、验证、准备、解析、初始化等阶段,以及类加载器的工作原理和双亲委派模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.类的加载时机

类加载过程: 加载-验证-准备-解析-初始化-使用-卸载

验证-准备-解析 通常称为连接阶段

虚拟机规范并没有规定class文件的类加载时机,但是严格规定了初始化的时机,而初始化之前一定要进行加载-验证-准备,解析则可以在初始化之前也可以在初始化之后。虚拟机规范规定了五种“当且仅当”的情况使类初始化(详细见Page210),需要注意的是数组对象只是创建了一个自动生成的类,不会初始化数组的元素对象,更不会调用类的构造函数。

2.类加载的过程

加载

  1. 获取class文件二进制流
  2. 将二进制流按照一定格式存储在方法区
  3. 在堆/方法区(HotSpot在方法区)中创建对应的Class对象,作为访问类信息的入口
    对于数组,其子类型也会被递归的加载,同时该数组类型将会和子类型的加载器建立关系

验证

验证阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害到虚拟机自身的安全
* 文件格式验证:验证Class字节流,保证文件格式正确,确保正确的存储到方法区(此处可以看出加载阶段和验证阶段同时进行,并且有先后关系),后面的三个阶段都是基于方法区存储结构进行的,不再操作字节流
* 元数据验证:分析字节码描述的语义,确保其符合java语法规范
* 字节码验证:针对方法体中方法的执行,通过数据流和控制流分析,确定语义是合法的、符合逻辑的,主要是字节码指令层面的验证,不同与元数据验证,元数据验证主要是语言层面的验证
* 符号引用验证:验证当前类所引用的外部信息是否正确,在解析阶段发生

准备

准备阶段主要对类变量(static修饰)进行赋初始值,如int是0,refrence是null,此外static final修饰的ConstantValue直接赋值成的它的值,如static final int a = 10则直接赋值成10

解析

将符号引用转换成直接引用的过程。动态语言要求解析在程序执行到这条指令的时候才进行解析,而相对的java是静态语言可以在刚刚加载完成还未执行代码时进行解析
1. 类或接口的解析
1. 若不是数组类型,则先加载该类,然后进行文件格式验证、元数据验证、字节码验证,后两个过程中可能会触发其他类的加载动作,若这个过程中出错,则解析失败。
2. 若是数组并且元素类型是对象,则将对元素类型进行上面的操作
3. 若上述步骤没有出现异常,则在方法区就已经有了一个有效的类或者接口了,最后还要进行符号引用验证,验证引用权限(符号引用验证)。
2. 字段解析
字段解析首先会对所属的类或者接口进行解析,若解析失败则字段解析失败,若成功才会继续解析字段

1. 先查找字段所属类/接口,是否有该字段,若有则返回直接引用
2. 从下往上递归查找所属类/接口实现的父接口是否有该字段,有则返回
3. 同样递归查找父类(Object除外)中是否有该字段,有则返回
4. 上述查找都未成功则查找失败
5. 若成功则进行字段符号引用的权限验证(符号引用验证)

>注意:若父接口和父类中存在同名的字段,则在编译阶段会

3. 类方法解析

注意:类方法解析和接口方法解析使用了不同的常量类型,一个是CONSTANT_Methodref_info,一个是CONSTANT_InterfaceMethodref_info。
首先类似字段解析,先解析方法所属的类,若解析成功才继续解析方法

1. 若发现索引到的是一个接口则抛出Error
2. 若该类中查找到该方法,则返回直接引用
3. 在父类中查找方法,若成功则返回
4. 在类实现的接口中查找方法,若存在方法,则这是一个抽象类,抛出AbstractMethodError异常
5. 否则抛出NoSuchMethodError
> 注意:最后进行类似字段解析的权限验证

4. 接口方法解析

类似的先进行方法所属接口的的解析

1. 若解析后索引的是一个类而不是接口,抛出Error
2. 若该接口中查找到该方法,则返回
3. 在父借口中查找直到Object类(字节码层面借口也继承了Object),找到则返回
4.否则抛出NoSuchMethodError

初始化

初始化是类加载的最后一个阶段,他会调用类构造方法(区分实例构造方法),初始化类变量(静态变量),执行静态代码块,这部分主要是一些语法的执行顺序。不赘述。详细见Page225。

2.类加载器

每个类,都由他的类加载器和class文件本身来确定唯一性,不同的类加载器加载相同的class文件是不同的类

三种系统提供的类加载器:
* Bootstrap ClassLoader启动类加载器 加载/lib
* Extension ClassLoader扩展类加载器 加载/lib/ext
* Application ClassLoader 应用程序类加载器 用户目录下类的默认加载器

双亲委派模型:

加载器加载类时调用loadClass()会先让父加载器进行优先加载,若父加载器加载成功,则结束,否则用Bootstrap ClassLoader进行加载,若加载成功则结束,否则调用findClass()方法进行加载,所以一般建议重写findClass()而不是loadClass()

双亲委派模型的破坏:
* 父加载器需要调用用户代码的API,此时java设计团队引入了线程上下文加载器(Thread Context ClassLoader),线程使用这个加载器可以调用用户代码,系统中的JNDI服务就是这么干的,这样系统的API调用了用户的代码,就破坏了双亲委派模型
* OSGi等模块化热部署实现了网状的类加载器结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值