基础 | JVM - [类加载器]
§1 类加载过程

装载
JVM 加载 class 文件到内存,并验证数据、解析、转化、初始化为 JVM 可以使用的 java 类型的过程
- 通过类全限定名获取类的二进制字节流
- 将字节流的内容转化为 JVM 运行时数据结构,存储到方法区
- java 堆中生成 java.lang.Class 对象,指向方法区,作为方法区数据访问入口
链接
验证
保证类的正确性
- 文件格式
- 元数据
- 字节码
- 符号引用
准备
为类中静态变量分配内存,但将其初始化为当前类型的默认值,即声明静态变量
比如 int -> 0 ,引用型 -> null
解析
动态的替换运行时常量池的符号引用,为具体的值
即,代码中对象 a 引用了对象 b,解析时,会替换掉 b 这个符号,而直接引用 b 对象的地址
初始化
初始化其实就是 Clinit() 的过程,包括如下环节
- 为静态变量赋值
- 初始化静态代码块
- 初始化当前类的父类
使用
lue
卸载
lue
§2 类加载机制
JVM 中其实包括 3 个类加载机制
- 双亲委派
- 全盘委派
- 全局缓存
§2.1 JVM 的类加载器
- Bootstrap
引导类加载器属于JVM的一部分,由 C++ 实现
负责加载 <JAVA_HOME>\jre\lib 下的核心类库
即,只加载 包名 java、javax、sun开头的类 - ExtClassLoader
Java语言实现,父(上级,并不是继承)加载器是Bootstrap启动类加载器
负责加载 <JAVA_HOME>\jre\lib\ext 下的类库 - AppClassLoader
父(上级,并不是继承)加载器是ExtClassLoader扩展类加载器
负责加载 classpath 环境变量所指定的类库,是用户自定义类的默认类加载器
§2.2双亲委派机制
过程
- AppClassLoader 检查缓存,已加载过就跳过,否则交由 ExtClassLoader 加载
- ExtClassLoader 检查缓存,已加载过就跳过,否则交由 Bootstrap 加载
- Bootstrap 检查缓存,已加载过就跳过,否则尝试从 Bootstrap 的负载目录中加载
- 不在 Bootstrap 的负载目录就交由 ExtClassLoader 加载
- 不在 ExtClassLoader 的负载目录就交由 AppClassLoader 加载
跳过双亲委派的方法
- 重写
ClassLoader.loadClass() - 重写
ClassLoader.findClass() - 使用线程上下文加载器
Thread.setContextClassLoader(),即 JVM 的 SPI 机制 - OSGI,用于热部署场景
§3 常见相关 API
ClassLoader 抽象类
除了 Bootstrap(它是 C++ 的),类加载器都要继承此抽象类
主要方法如下
| 方法名 | 作用 | 备注 |
|---|---|---|
| public Class<?> loadClass(String name) | 双亲委派机制的实现 | |
| protected Class<?> findClass(String name) | 读取字节码文件到内存后通过 defindClass 方法 | 直接调用此方法可以跳过双亲委派 |
| protected final Class<?> defineClass(String name, byte[] b, int off, int len) | 先判断是否加载过,然后将字节数组解析成 Class 对象 | |
| protected final void resolveClass(Class<?> c) | 连接指定的类 |
沙箱安全机制
虚拟机会把所有代码加载到不同的系统域和应用域。系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域(Protected Domain),对应不一样的权限(Permission)。存在于不同域中的类文件就具有了当前域的全部权限,如下图所示

本文部分内容来自 java类加载器、双亲委派、沙箱安全机制全都让你整明白

浙公网安备 33010602011771号