JVM虚拟机之类加载机制

本文详细介绍了JVM类加载的过程,包括加载、验证、准备、解析、初始化、使用和卸载七个阶段。重点讨论了类的初始化条件,以及类加载器的工作原理,特别是双亲委派模型。此外,还提到了类加载器的破坏情况,如OSGI和SPI加载。

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

JVM虚拟机之类加载机制

在本节中,学习的是Class文件如何进入虚拟机。

类从加载到卸载分成七个阶段。加载,验证,准备,解析,初始化,使用,卸载。其中的验证,准备,解析统称为连接。

对类的初始化常常是建立在类或子类被直接使用的情况下,如new,反射调用,子类被使用时,启动类,静态方法的调用。其他情况一律不考虑初始化。

类加载

首先对类进行加载,通过全限定名进行类的二进制字节流获取,之后将字节流的存储结构化成方法区的数据结构,存储在方法区中,生成Class对象,作为以后使用其方法的入口。

对于二进制字节流的获取并不严格,因此可以从各种途径获取,此处不进行介绍。

对于数组的加载比较特殊,因为数组是由虚拟机直接生成的,但数组的元素与普通对象一致,生成过程并无不同。

验证

验证阶段需要保证Class文件符合要求。

1.基本格式验证

例如Magic Number,版本号,是否符合UTF-8编码,文件缺不缺东西之类的、

2.元数据验证

顾名思义,进行语义校验,例如有没有父类,父类是Final类之类的。

3.字节码验证

字节码验证是最复杂的阶段,他需要保证字节指令的正确性,例如,不会随便跳转,不会指令操作错误等,如前文中的StackTable就是用来字节码检验的,它将对字节码的推导转换成了对StackTable的检验,它保证了操作栈与本地变量表在某一块区域开始时处在正确的位置。

4.符号引用验证

保证符号引用是对的,能找到,也能被访问,方法等也都存在。虚拟机参数提供了取消验证的参数。

准备

准备动作是将类中的变量在方法区中分配内存并进行赋值,初值全部为0,由ConstantValue属性表的按照表中数据进行赋值。

解析

通过解析将长长的符号引用变成指针类型的直接引用。对于已经解析的符号引用也提供的缓存标记,在下次可以直接使用,invokedynamic指令的除外,因为是动态的,每次都需要重新解析。

其他类或接口的符号引用解析

先解析其他的类或接口,这时会触发其他类的验证,其他类又会触发其他类,如此反复,一个出现错误全错。如果想要变成对数组的引用,比如int数组,那么按照之前的,对Integer进行验证,之后由虚拟机自己生成一个数组。

字段解析

对于字段,首先查找字段表,找到所属类,对类进行解析,之后根据类中数组,查找有无该字段,返回直接引用,如果没有,之后会找接口,父类,从下到上,全都没有失败结束。

返回之后,如果这个字段你没有访问权限的话也是失败的。抛出异常。

对于类与接口方法的解析其实是差不多的,首先不允许方法所对应的类不同,类方法对应的不允许是接口,接口方法对应的类不允许是类。之后,在本类(接口)与父类(父接口)进行查找。最后进行访问权限检查,接口不需要进行该检查。

类与接口方法解析

两种解析是差不多的。

1.查询方法表中的本类。类方法表中的本类必须是类,不能是接口。接口方法表中的本类必须是接口,不允许是类。

2.查询本类(接口)中是否有该方法。

3.查询父类(父接口)

类要进行访问权限检查。接口并不需要。

初始化

在初始化的时候会根据变量赋值代码与static语句自动生成方法。

clinit方法会自动隐式的调用父类的clinit方法。对于接口与接口的实现类,并不会主动调用父类clinit方法,只有用到的时候才会调用。

对于static语句块只能访问语句块之前的,定义在之后的只能赋值不能访问。

同时,初始化保证了多线程的同步。

-----------类加载器

类加载器用在类加载的部分。是Java的创新。

类加载器的作用在于加载类,确认类的唯一性

这个唯一性在于,如果不是同一类加载器加载出来的,一定不是同一类,即使Class文件一模一样。

双亲委派模型

类加载器只有两种:Booststrap(启动类加载器),与其他(基于代码实现)。

BoostStrap在HotSpot中采用C++实现,其他虚拟机通过Java代码实现或者JNI调用C语言实现。

对于开发人员细分为三种:

BoostStrap启动类加载器:

仅根据文件名识别指定目录(lib)下的类加载到内存当中,由于是虚拟机内部的,因此无法被直接调用,如果你想让这个类被BoostStrap加载,直接加载请求为null即可。

扩展类加载器

加载lib/ext下的类库。

应用程序类加载器

getSystemClassLoader的返回值,为默认加载器。检索ClassPath路径下的类。

根据双亲委派模型,所有的类加载行为都默认优先由父类进行加载。若要自定义类加载器。需要重写findclass函数。

OSGI:破坏了双亲委派模型。变成了网状模型,优先调用父类,在父类都查询不到的时候会进行同级的类加载器进行,因此不符合双亲委派。

对于加载SPI代码时,也破坏了双亲委派模型,需要线程上下文类加载器进行逆向的使用类加载器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值