JVM类重复加载报错本质是同一类被不同ClassLoader加载导致链接约束违反;表现为LinkageError、NoSuchMethodError等,关键依据是同名类但ClassLoader不同即视为无关类型,需用-verbose:class、mvn dependency:tree、Arthas等工具定位并按环境排除冗余依赖或调整类加载策略。

JVM 类重复加载报错,本质不是“类找不到”,而是“类被多个 ClassLoader 各自加载了一次”,导致链接阶段违反 JVM 的类型一致性约束。这类问题常表现为 NoSuchMethodError、IncompatibleClassChangeError、LinkageError: loader constraint violation 或诡异的 ClassCastException(哪怕类名、包名、源码完全一样)。关键判断标准只有一个:两个同名类对象,只要 ClassLoader 不同,在 JVM 看来就是两个完全无关的类型。
看日志确认是否真被重复加载
启动时加 JVM 参数:-verbose:class,运行后 grep 关键类名(如 com.example.Service):
- 若同一类名出现多条
[Loaded com.example.Service from ...],且路径不同(比如一个来自app.jar,另一个来自tomcat/lib/servlet-api.jar),说明已被不同加载器加载 - 注意首次加载记录——JVM 通常只用第一个,但后续若其他加载器强行 loadClass,就可能触发冲突
- 配合 -XX:+TraceClassLoadingPreorder 可看加载优先级顺序,辅助判断谁“赢了”
查构建产物里有没有多个 class 副本
重复加载往往源于 classpath 中存在多个同名 class 文件:
- Maven 项目执行:
mvn dependency:tree -Dincludes=groupId:artifactId,检查是否有多个模块引入含目标类的 jar - 打包后进
target/目录,用jar -tf app.jar | grep Service.class查是否被打包多次 - 解压 fat-jar 或 war,执行:
find . -name "Service.class",看是否散落在多个 jar 或 classes 目录下 - IDE 中 Ctrl+Click 类名,若弹出多个跳转候选,说明工程内已存在多版本源或 class
用工具定位实际加载来源和加载器
仅看依赖树不够,要看到运行时真实加载行为:
- Arthas 快速诊断:
sc -d com.example.Service显示该类被哪些 ClassLoader 加载过,附带 jar 路径 - 反编译验证字节码:
javap -cp xxx.jar -s com.example.Service对比方法签名、字段描述符,确认是不是“同名不同质” - 在异常 catch 块中加调试语句:
System.out.println(clazz.getClassLoader()),打印出错类的加载器实例 - JMX 或
jcmd <pid> VM.native_memory summary</pid>辅助观察类加载上下文
按场景选择修复方式
没有通用解法,需结合部署环境决定策略:
-
Web 容器(Tomcat/Jetty):把共用库(如 slf4j-api、servlet-api)移到
$CATALINA_HOME/lib,并从WEB-INF/lib中彻底删除对应 jar -
Spring Boot Fat Jar:检查是否嵌入了与启动类加载器已加载版本冲突的核心类(如 logback、spring-core),用
mvn dependency:tree找出高版本传递依赖,通过<exclusion></exclusion>排除 -
自定义 ClassLoader:确认是否违反双亲委派——子加载器应先委托父加载,避免父已加载却自己又 load 一遍;检查是否误用了
Thread.currentThread().getContextClassLoader() -
多模块共享接口(如函数式接口):禁止各模块自行定义同名接口,统一收敛到专用
domain-func模块,并用 Maven Enforcer 插件的banDuplicateClasses规则在构建期拦截


















