JVM(三):类加载机制

1.JVM类加载的五个过程

  • 加载
  • 验证
  • 准备
  • 解析
  • 初始化

A.加载

加载有几步:

  • 通过类的全限定名获取该类的字节流
  • 在内存中生成一个这个类的Class对象
  • 将流储存到方法区中

所加载类的来源有许多种:从Class文件中获取,也可以从jar或war包中获取,又或者在运行时通过动态代理生成,也可以从网络中获取,当然也可以是由JSP文件转换为对应的Class类。

B.验证

JVM会验证刚刚加载的类是否符合虚拟机的要求,是否有正确的内部结构,同时也是为了保护虚拟机。
主要包括四种验证:

  • 文件格式的验证:验证class文件是否符合正确格式,例如版本号是否是这个JVM能处理的,常量类型是否支持。
  • 元数据验证:字节码是否符合Java语言语法规范,例如父类是否继承了不允许被继承的类。
  • 字节码验证:验证类的方法体是否正确
  • 符号引用验证:引用是否能找到正确的目标,即验证是否能通过引用访问到对象。

C.准备

在方法区中为类变量(static)分配内存并根据类型设置对应的默认值。

需要注意的是,这里的默认值不是指显式赋予的值,显示赋值在之后初始化时赋予,例如

private static int i = 5;

默认值不是这个5而是0,不同的数据类型有不同的默认值:

数据类型默认值
int0
long0L
short0(short)
booleanfalse
float0.0f
double0.0d

但是如果类变量被final和static同时修饰了,在准备阶段就会赋予显式赋值的初始值

例如

private static final int i = 5;

D.解析

在解析阶段,JVM会将常量池中的符号引用替换为直接引用。别处看到的有关符号引用和直接引用的解释:

符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。

直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在

可以理解为,符号引用就是一个昵称,不会冲突,我们能通过昵称知道这个人是谁;而直接引用就是一个指向这个人的指针,这个人一定在指针所指在的地址。

E.初始化

这是类加载的最后一个阶段,这个阶段执行类构造器 < client >()方法,会为类的静态变量赋予正确的初始值,并调用静态代码块。

在通过new创建对象、访问类的静态变量或方法、通过Class.forName加载类时,都会执行类的初始化。

2.JVM类加载器

类加载器(ClassLoader)负责将类加载到JVM中,而且还会判断每个类应该由什么加载器来加载。

A.ClassLoader类

ClassLoader类中有一个方法

defineClass(byte[] b, int off, int len)

这个方法可以将传入的byte字节流解析为JVM能识别的Class对象,正是因为有了这个方法,我们在加载阶段可以有多种所加载类的来源而不限于Class文件。

B.类加载器 jdk8

JVM提供了三种类加载器:

  • Bootstrap ClassLoader
  • Extension ClassLoader
  • Application ClassLoader
Bootstrap ClassLoader

Bootstrap ClassLoader负责加载JAVA_HOME\lib中的被虚拟机认可的类,或者通过-Xbootclasspath参数指定路径的类。

Extension ClassLoader

Extension ClassLoader负责加载JAVA_HOME\lib\ext目录中的类,或者通过java.ext.dirs系统变量指定路径中的类。

Application ClassLoader

Application ClassLoader负责加载用户路径(classpath)上的类库,我们也可以继承java.lang.ClassLoader实现自定义的类加载器。

补充

在jdk10中,使用PlatformClassLoader来替代Extension ClassLoader。

D.双亲委派机制

ClassLoader的加载机制是一个向上级委托的机制,当一个类需要被加载的时候,会将这个请求委派给父类加载器,并进行判断:

  • 如果父类加载器不能完成对这个类的加载,那么该加载器就会对类进行加载
  • 如果父类加载器可以完成这个请求,那么父类加载器也会同样的向更上一级的加载器去请求,每一层的加载器都会这么做,直到上一级不能完成这个请求为止。

假设类A有两个子类类B和类C,且类B和类C分别由不同的类加载器加载,采用双亲委派机制,就保证了无论通过类B还是类C去加载父类A,都会委托给类A对应的类加载器加载,保证得到的是一样的类A。

最简单的例子就是无论什么加载器加载Object类,通过这个机制,都会委托给顶层的Bootstrap ClassLoader去加载,保证得到同一个Object。

3.类加载异常

类加载有几个常见的异常:

  • ClassNotFoundException:使用显式加载类的时候,如Class的forName方法,未能通过类的名字找到对应的类,就会抛出这个异常
  • ClassCastException:类型转换错误的异常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值