classloader 的 classes 缓存表由每个 classloader 实例自身维护,使用 concurrenthashmap 存储全限定名到 class 对象的映射,通过 final 方法 findloadedclass() 访问,仅在 defineclass 与 resolveclass 成功后由 jvm 自动注册写入,不可主动清除。

Java JVM 运行时的类加载器缓存机制,核心是每个 ClassLoader 实例自带一个内部缓存表,用于避免重复加载同一类,提升性能并保证类唯一性。它不是全局共享的哈希表,而是按加载器隔离、线程安全、只读查询的设计。
ClassLoader 的 classes 缓存表由谁维护?
每个 ClassLoader 子类(包括 AppClassLoader、ExtClassLoader 和自定义加载器)都继承了父类中已实现的缓存逻辑:
- 内部使用一个
ConcurrentHashMap<string class>></string>(JDK 8+),键为类的全限定名(如"java.lang.String"),值为已加载的Class对象; - 该缓存由
findLoadedClass(String name)方法封装访问,是 public final 方法,不可重写; -
findLoadedClass0(name)是 native 方法,实际查的就是这个 map —— 它在ClassLoader构造时初始化,生命周期与加载器一致。
缓存何时被写入?
只有当类成功完成整个加载流程(加载→验证→准备→解析→初始化)后,才会写入缓存:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 调用
defineClass()生成Class对象; - 紧接着调用
resolveClass()(若需解析); - 最终由 JVM 在
ClassLoader.registerAsParallelCapable()后自动触发addClass()(内部注册),将Class放入缓存; - 注意:
loadClass()方法本身不直接写缓存,而是委托给findClass()→defineClass()链路完成后才入库。
缓存能否被清除或绕过?
不能主动清空,也不建议干预:
- 没有公开 API 清除单个类或全部缓存(
clearCache()不存在); - 卸载类的前提是:该
ClassLoader实例本身被 GC 回收(且其加载的所有类都不再被引用); - 手动调用
System.gc()不会触发类卸载,仅当类加载器变成垃圾对象,且 Metaspace 中对应 Klass 元数据无强引用时,才可能随类加载器一起回收; - 若想“刷新”类(如热部署),必须创建全新 ClassLoader 实例,旧实例及其缓存自然失效。
常见误操作与风险
- 重写
findLoadedClass()?→ 编译失败,它是final方法; - 在
findClass()中提前返回已有Class?→ 可能跳过验证/初始化,导致NoClassDefFoundError或IllegalAccessError; - 多线程并发加载同一类?→
ConcurrentHashMap保证线程安全,首次成功加载者胜出,其余线程拿到同一个Class实例; - 使用
Class.forName(name, false, loader)?→false表示不触发初始化,但只要类已加载,仍走缓存路径,不会重复 define。
基本上就这些。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










