当应用程序试图调用类(静态或实例)的指定方法,而该类已不再具有该方法的定义时,就会抛出 java.lang.NoSuchMethodError 错误。简单地说,就是同一个 Class 有多个版本的实现,并且在运行时调用了缺少方法的那个版本。本文总结了 NoSuchMethodError 常见原因及其解决方法,如有遗漏或错误,欢迎补充指正。

运行时抛出 NoSuchMethodError 的根本原因是什么?


在实际生产系统中,我们主要关注运行时抛出的 NoSuchMethodError 错误,该错误轻则导致程序异常终止,严重时甚至会产生不可预知的程序结果,比如支付服务执行异常,实际支付已完成,却向用户返回支付失败。

运行时抛出 NoSuchMethodError 错误的根本原因就是:应用程序直接或间接依赖了同一个类的多个版本,并且在运行时执行了缺少方法的版本。如下图所示:

因此,核心问题就转化为:同一类为什么会有多个版本?哪个版本的类最终会被执行?

为什么同一个 Class 会出现多个版本?


导致 Java Class 出现多版本的原因,可以归纳为以下几类:

  • JDK 版本不一致。常见于编译打包环境使用高版本 JDK 开发与打包,而实际运行环境的 JDK 版本较低。例如,本地项目环境 JDK 版本为 1.7,调用 Character.isAlphabetic() 方法判断当前字符是否为字母;而线上环境 JDK 版本为 1.6,在运行期间就会抛出 NoSuchMethodError 错误。

  • SNAPSHOT 版本不一致。常见于本地更新 SNAPSHOT 版本后,没有执行 mvn clean deploy 部署,导致线上环境运行时仍然引用了旧版本的 SNAPSHOT 包。

  • Maven 依赖生命周期为 provided。常见于本地依赖的某组件生命周期为 provided,所声明版本仅用于本地编译打包,而线上运行时会通过其他依赖关系加载 Jar 包。

  • 同一个 Jar 包出现了多个版本。常见于 Maven 依赖未显式指定版本号,导致间接依赖版本冲突,很容易引入低版本的 Jar 包。

  • 同一个 Class 出现在不同的 Jar 包中。该问题常见于代码拷贝场景,比如基于开源版本定制了一些功能,使用了新的 Maven 坐标打包发布,此时 Maven 仲裁机制失效(非常隐蔽,难以排查)。由于 JVM 类加载器对于同一个类只会加载一次,最终加载的类实现受到 Jar 包依赖的路径、类声明的先后顺序或文件加载顺序等因素的影响,很可能出现不同机器加载的类实现不一致。

哪个版本的 Class 最终会被执行?


影响 Class 最终是否被执行的关键因素有两个:Maven 依赖仲裁机制和 JVM 类加载机制,如下图所示:

首先,Maven 依赖仲裁机制 决定了打包的优先级, 仲裁优先级“从高到低”如下所述:

  • 优先按照依赖管理 [dependencyManagement] 元素中指定的版本进行仲裁;

  • 若无版本声明,则按照 “短路径优先” 原则(Maven2.0)进行仲裁,即选择依赖树中路径最短的版本;

  • 若路径长度一致,则按照 “第一声明优先” 原则进行仲裁,即选择 POM 中最先声明的版本。

合理使用 Maven 依赖仲裁机制可以便捷的管理 Jar 包版本,而不合理的使用将导致多版本 Jar 冲突。

其次,JVM 类加载机制 决定了 Class 被加载到 JVM 的优先级, 如果同一个类出现在多个 Jar 包中,那么在 双亲委派类加载机制 下,加载该 Jar 包的类加载器层级越高,该 Jar 包越先被加载,它所包含的 Class 越先被执行,如上图所示:

  • 启动类加载器(Bootstrap ClassLoader)优先级最高,主要加载 JVM 运行时核心类,如 java.util、java.io等,这些类主要位于 $JAVA_HOME/lib/rt.jar 文件中。

  • 扩展类加载器(Extention ClassLoader)优先级次之,主要加载 JVM 扩展类,如 swing 组件、xml 解析器等,这些类主要位于 $JAVA_HOME/lib/ext/ 目录下的 Jar 包中。

  • 应用类加载器(Application ClassLoader),又称系统类加载器,优先级再次之,它会加载 Classpath 环境变量里定义的路径中的 Jar 包和目录,通常我们自己编写的代码或依赖的第三方 Jar 包都是由它来加载。

除了上述两种原因外,在同一个 ClassLoader 下,如果存在一个 Class 出现在不同的 Jar 包中,那么文件系统的文件加载顺序也可能会影响最终的加载结果。因此,应该尽量保证开发/测试/生产系统环境一致性。

如何解决 NoSuchMethodError 错误?


虽然抛出 NoSuchMethodError 错误的原因多种多样,但本质上是由于编译时类路径与运行时类路径不一致。因此,通用的定位思路可以归纳为以下 3 步:

1、定位异常 Class 的全限定类名与调用方,通常可以在应用日志抛出的异常堆栈中获取。如下图所示:

Exception in thread "main" java.lang.NoSuchMethodError: com.xxx.AsyncAppender.append(Ljava/lang/String;)Ljava/lang/String;  at com.xxx.ProvokeNoSuchMethodError.main(ProvokeNoSuchMethodError:7)  at ……

2、定位异常 Class 的来源,可以通过 Arthas 等在线诊断工具反编译,如 jad com.xxx.AsyncAppender,获取该类运行时的源码、ClassLoader、Jar 包位置等信息。

1、根据 ClassLoader 和 Jar 包全路径名等信息,判断是类加载、Maven 仲裁或其他原因,并对应的加以解决。例如加载了同一个 Jar 包的低版本实现,则在 Maven dependencyManagement 中指定版本,或者移除间接依赖中的低版本(提示: 执行 mvn dependency:tree 命令,可以输出 Maven 依赖拓扑关系)。

其他 Jar 包冲突问题


本文介绍的 Jar 包冲突解决方法,除了解决 java.lang.NoSuchMethodError 以外,对其他相似问题也具备一定的参考价值。

例如 java.lang.ClassNotFoundException,即加载不到指定类,通常是 Maven 仲裁选错了版本,如本地开发阶段调用了 1.2.0 版本,而打包时采用了 1.0.0 版本的 Jar 包。同理,java.lang.NoClassDefFoundError 和 java.lang.LinkageError 也可以基于上述思路进行排查。

此外,如果类和方法名都保持不变,但是内部实现有变化,在多版本冲突场景下,不会抛出异常,但程序行为跟预期不一致, 此时,也可以基于上述思路进行排查诊断。

参考文章


  • 重新看待 Jar 包冲突问题及解决方案

    http://www.yangbing.club/2017/07/15/solution-for-jar-conflicts/

  • 3 Steps to Fix NoSuchMethodErrors and NoSuchMethodExceptions
    https://reflectoring.io/nosuchmethod/

作者信息:

夏明,GitHub ID @StabilityMan,花名涯海,阿里云 ARMS & EagleEye 技术专家,2016 年加入阿里巴巴,一直从事链路追踪和 APM 监控诊断领域的相关工作。

本文缩略图:icon by 轩坊

NoSuchMethodError 发生原因和解决办法相关推荐

  1. 你真的知道 NoSuchMethodError 发生原因和解决办法吗

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群"加入公众号专属技术群 来源:阿里巴巴中间件 当应用程序试图调用类(静态或实例)的 ...

  2. NoSuchMethodError 的发生原因和解决办法

    当应用程序试图调用类(静态或实例)的指定方法,而该类已不再具有该方法的定义时,就会抛出 java.lang.NoSuchMethodError 错误.简单地说,就是同一个 Class 有多个版本的实现 ...

  3. 富士通打印机调整位置_打印机为什么卡纸 打印机四种卡纸原因及解决办法【介绍】...

    我们在使用打印机的过程中,由于频繁使用打印机,常常会出现打印机无法处理纸张的故障.大多数常见的故障就是夹纸.不进纸.一次进多页纸以及乱走纸等.我们在打印机出现这些现象的时候,该怎样去应对呢?下面为大家 ...

  4. [J2ME]Nokia播放音乐时发生MediaException的解决办法

     [J2ME]Nokia播放音乐时发生MediaException的解决办法 现象 在Nokia7610上播放音乐时遇到了-18的MediaException错误! J2ME中播放音乐资源的代码很容易 ...

  5. compress后的bytearray再decode变大_笔记本电脑风扇噪音变大的原因及其解决办法

    笔记本使用时间长了之后,风扇声音会变大,那么如何再把风扇声音变小呢?怎么减小笔记本风扇的噪音?是什么原因导致笔记本风扇声音变大?下面给大家介绍笔记本风扇声音变大的原因及其解决办法. 原因: 1.使用时 ...

  6. 有关域索引错误产生的原因及解决办法

    1说明 数据库错误ORA-29861:域索引标记为LOADING/FAILED/UNUSABLE,其错误原因及解决办法,根据ORACLE官方文档的说法如下: // *Cause: An attempt ...

  7. Session莫名丢失的原因及解决办法[转载]

    Asp.net 默认配置下,Session莫名丢失的原因及解决办法 正常操作情况下Session会无故丢失.因为程序是在不停的被操作,排除Session超时的可能.另外,Session超时时间被设定成 ...

  8. mysql数据库什么情况下会锁表_mysql数据库锁的产生原因及解决办法

    数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性 ...

  9. mysql数据库死锁的产生原因及解决办法

    该文章为转载,如有侵权请及时联系 这篇文章主要介绍了mysql数据库锁的产生原因及解决办法,需要的朋友可以参考下 数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据 ...

最新文章

  1. 2017年度最受欢迎开源中国项目:roncoo-pay投票评选
  2. 如何使Layouts里的页面应用站点母板页
  3. Matrix PKU 2155
  4. c# 插入树形数据#_C#数据类型能力问题 套装1
  5. 11月24日 layouts and rendering in rails(部分没有看)
  6. creo打不开stp文件_让Creo输出的stp格式文件含有颜色设置 | 坐倚北风
  7. mysql续型_mysql续集(查询部分)
  8. 用Redis实现Session功能
  9. PHP文件流下载内容
  10. svn开发环境代码合并到生产
  11. windows 下载 gcc
  12. SVN下载项目到本地
  13. 学习计算机英语的重要性
  14. 记一次修复Mac和Win7双系统启动菜单的经历
  15. win10服务器怎么连接显示器不亮,win10开机后显示器不亮如何解决_win10开机后显示器不亮的处理方法...
  16. 2020线上夏令营感受
  17. Linux内核基础--事件通知链(notifier chain)good【转】
  18. 2020.3.23 bugku(21-25)
  19. r语言mfrow全程_R语言(绘图入门)
  20. linux中可以使用以下命令查看文件内容,在Linux服务器中使用命令行中查看文件内容...

热门文章

  1. HDU多校6 - 6831 Fragrant numbers(dfs爆搜+打表)
  2. 牛客 - 树上博弈(思维)
  3. 从蓝桥杯来谈Fibonacci数列
  4. 视音频数据处理入门:AAC音频码流解析
  5. ADO学习(十)DataGrid控件的使用
  6. OkHttp3的连接池及连接建立过程分析
  7. linux 下 MySQL卸载和安装
  8. LeetCode解题的常见模式套路
  9. 面试题鬼的很:Class.forName 和 ClassLoader 有什么区别?
  10. 为什么你不应该接受有 race 的代码