JVM类加载缓存机制分为两级:第一级是ClassLoader实例的ConcurrentHashMap缓存,以类全限定名为键、Class对象为值,命中则跳过加载;第二级是Metaspace元数据缓存,跨加载器共享Klass等结构,降低内存开销并加速反射访问。

JVM 的类加载缓存机制通过两级缓存协同工作,核心是避免重复加载、验证和解析同一类,从而显著减少 I/O、CPU 和内存开销。它不是额外附加的功能,而是内嵌在类加载流程中的默认行为。
类加载器实例级缓存(第一级)
每个 ClassLoader 实例 内部维护一个线程安全的缓存(HotSpot 中为 ConcurrentHashMap),以类的全限定名(如 java.lang.String)为键,已加载的 Class 对象为值。
- 调用
ClassLoader.loadClass()或Class.forName()时,JVM 首先执行findLoadedClass(String name)方法查询该缓存; - 若命中,直接返回已有
Class对象,跳过后续所有加载步骤; - 若未命中,才进入双亲委派流程——先委托父加载器查找,父加载器查不到再由当前加载器尝试加载;
- 注意:不同类加载器实例即使加载同一个类名,也会产生不同的
Class对象(这是类隔离的基础)。
Metaspace 元数据级缓存(第二级)
类成功加载并验证后,其结构信息(Klass、常量池、方法字节码、字段描述等)被写入 JVM 全局的 Metaspace,这部分缓存是跨类加载器共享的(但受类加载器生命周期约束)。
- Metaspace 不缓存
Class对象本身(它在堆中),而是缓存底层元数据,比如符号引用解析后的直接引用、常量池项、方法入口地址等; - 当多个类加载器加载相同字节码(如系统类
java.lang.Object),它们各自持有的Class对象指向 Metaspace 中同一份 Klass 结构; - 这种复用大幅降低元数据重复存储的内存压力,也加速反射、动态代理等对元数据的高频访问。
缓存如何参与标准加载流程
以 Class.forName("com.example.Service") 为例,缓存介入的关键节点如下:
立即学习“Java免费学习笔记(深入)”;
- 当前线程上下文类加载器调用
findLoadedClass("com.example.Service")→ 缓存未命中; - 触发双亲委派:先由 AppClassLoader 委托给 ExtClassLoader,再委托给 BootstrapClassLoader;
- Bootstrap 和 Ext 均未找到,AppClassLoader 自行从 classpath 加载字节码;
- 完成验证、准备、解析后,将元数据写入 Metaspace,并在自身缓存中注册
Class对象; - 下次再加载同名类,无论是否同一调用点,只要使用同一个类加载器实例,就直接命中第一级缓存。
缓存带来的实际影响
缓存机制让 JVM 在运行期保持类的一致性和高效性,但也带来一些必须注意的行为特征:
- 类一旦加载进 JVM,就不会被卸载(除非其类加载器被 GC 回收),因此热更新需替换整个加载器;
- 修改 .class 文件后不重启 JVM,新字节码不会生效——因为缓存始终返回旧
Class对象; - 自定义类加载器若未正确复用或覆盖
findLoadedClass,可能绕过缓存,引发重复加载甚至LinkageError; - 频繁动态生成类(如大量 CGLIB 代理)会持续占用 Metaspace,需合理设置
-XX:MaxMetaspaceSize并监控使用量。


















