跳至主要內容

Java虚拟机类加载过程

原创GuoCay约 957 字JavaJVM

Java虚拟机类加载过程
Java虚拟机类加载过程

loadClass(): 对应加载阶段

根据类名获取锁对象, 并通过synchronized加锁.

这个加锁操作保证了只会有一个线程对当前类进行加载, 解决了多线程重复加载的问题.

findLoadedClass(): 在JVM方法区找一下是否存在当前Class对象并返回.

checkName(): 查验类名是否符合规范

其实就是看这个字符串是否符合java类名命名规范.

findLoadedClass0(): native函数, 从方法区查验当前类是否存在.

如果上一步没找到Class类

记录当前时间

查看是否设置了父加载器

找到了, 调用父加载器的loadClass()
  • 这一步也保证了类的单一加载器加载, 也保障了类的静态代码块有且仅运行一次.

  • 这一步是双亲委派实现的原理.

findBootstrapClassOrNull(): 没找到, 尝试调用 BootstrampClassLoader加载当前类.
checkName(): 校验名称是否合法, 不合法则返回.
findBootstrapClass(): native函数, 通过 BootstrapClassLoader加载类.

如果上一步还是没找到.

再次记录当前时间.
调用当前类加载器的findClass()获取类.
记录类加载器指标
记录去父加载器中查找的耗时
记录加载器开始查找时间
记录加载类找到的类个数.

resolveClass(): 如果设置需要解析, 则校验Class对象.

就是看一下这个类是否还是空,是则抛出空指针异常.

defineClass(): 对应链接阶段(验证, 准备, 解析)

preDefineClass(): 定义类前的处理逻辑(确定保护域)

checkName(): 检查类名

校验类名不以"java."开头,且不是PlatformClassLoader(这个类在jdk8前叫ExtClassLoader)

否则抛出安全异常

如果保护域对象为空, 则赋值为默认保护域.

checkCerts(): 认证当前类所属的包是否在签名内.否则抛出安全异常

defineClassSourceLocation(): 定义类资源位置

从ProtectionDomain的CodeSource中获取位置信息.

defineClass1(): native函数, 将二进制数组转换为一个Class对象

验证(可以通过虚拟机关闭验证阶段以加快类的加载. 但不建议这么做. 折中的方法可以让虚拟机预热类)

文件格式验证

魔数开头, JVM版本号...

元数据验证

是否有继承链, 继承是否合法...

字节码验证

最复杂的部分, 验证指令是否合法, 指针是否合法, 不会对虚拟机造成伤害...

符号引用验证

将符号引用转换为实际引用(伴随着解析部分),并校验是否合法.

准备

为类中的静态变量开辟内存空间.

解析

接口(类)解析

接口在方法区的内存位置信息.

字段解析

将函数或类中用到的属性的相对偏移量转换出来. 比如, 指向常量池中的实际地址.

方法解析

就是将类的元信息在方法区内存中的偏移量赋给方法区中的类.供对象后续调用时使用. 这是一个内存地址信息(JVM管理的内存地址)

初始化(非构造函数)

  • 收集当前类中的静态代码块信息, 并执行.
  • 收集编译器生产的赋值操作后,生成的静态代码.并执行.

postDefineClass(): 定义类后的处理逻辑

getNamedPackage(): 定义一个命名包,放到当前ClassLoader中, 如果不存在.

如果安全域中的CodeSource中的签名不为空.则将签名放到当前class类中.

setSigners(): native函数.