JVM类加载机制
大约 4 分钟
1. 类加载过程
1.1 类的生命周期
类从被加载到虚拟机内存中开始到卸载出内存为止,它的整个生命周期可以简单概括为 7 个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)。其中,验证、准备和解析这三个阶段可以统称为连接(Linking)
1.2 类的加载过程
类生命周期中的加载、验证、准备、解析和初始化这五步即为类的加载过程
1.2.1 加载
- 通过全类名获取定义此类的二进制字节流
- 字节流所代表的静态存储结构转换为方法区的运行时数据结构
- 在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口
1.2.2 验证
确保Class 文件的字节流中包含的信息符合《Java 虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机的安全
1.2.3 准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段
1.2.4 解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程
1.2.5 初始化
初始化阶段是执行初始化方法 <clinit> ()
方法的过程,是类加载的最后一步,这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)
1.3 类的卸载
类的卸载需要满足三个要求:
- 对象被GC:该类的所有的实例对象都已被 GC,也就是说堆不存在该类的实例对象。
- 无引用:该类没有在其他任何地方被引用
- 类加载器被GC:该类的类加载器的实例已被 GC
2. 类加载器
2.1 介绍
每个Java类都有一个引用指向加载自身的ClassLoader
(数组类由JVM直接生成)。类加载器的主要作用是加载Java类的字节码(.class文件)到JVM中。
2.2 JVM类加载器
JVM内置了三个重要的类加载器:
BootstrapClassLoader
(启动类加载器):最顶层的加载类,由 C++实现,通常表示为 null,并且没有父级,主要用来加载 JDK 内部的核心类库(%JAVA_HOME%/lib
目录下的 rt.jar、resources.jar、charsets.jar等 jar 包和类)以及被 -Xbootclasspath参数指定的路径下的所有类ExtensionClassLoader
(扩展类加载器):主要负责加载加载扩展类,%JRE_HOME%/lib/ext
目录下的 jar 包和类以及被java.ext.dirs
系统变量所指定的路径下的所有类AppClassLoader
(应用程序类加载器):面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类
除此之外,也可以自定义类加载器继承自ClassLoader
。比如对class进行加密,并用类加载器进行解密。
ClassLoader
类中有两个关键方法:
protected Class loadClass(String name, boolean resolve)
:加载二进制名称的类,实现了双亲委派机制protected Class findClass(String name)
:根据类的二进制名称来查找类,默认为空
如果不想打破双亲委派模型,只需重写findClass即可。这样会自动通过父类加载,如果未被加载的话则会调用该方法加载
2.3 双亲委派模型
- 双亲委派模型具有以下机制:
- ClassLoader 类使用委托模型来搜索类和资源
- 双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器
- ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器
- 双亲委派的执行过程如下:
- 首先判断是否被加载过,若未被加载过会尝试加载
- 加载时调用父类的
loadClass()
来加载,最终会到达BootstrapClassLoader
- 若父类无法加载,会返回并调用子类的
findClass
来加载 - 如果都无法加载则抛出异常
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//首先,检查该类是否已经加载过
Class c = findLoadedClass(name);
if (c == null) {
//如果 c 为 null,则说明该类没有被加载过
long t0 = System.nanoTime();
try {
if (parent != null) {
//当父类的加载器不为空,则通过父类的loadClass来加载该类
c = parent.loadClass(name, false);
} else {
//当父类的加载器为空,则调用启动类加载器来加载该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//非空父类的类加载器无法找到相应的类,则抛出异常
}
if (c == null) {
//当父类加载器无法加载时,则调用findClass方法来加载该类
//用户可通过覆写该方法,来自定义类加载器
long t1 = System.nanoTime();
c = findClass(name);
//用于统计类加载器相关的信息
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//对类进行link操作
resolveClass(c);
}
return c;
}
}