具体参考自https://blog.csdn.net/changerzhuo_319/article/details/78925184

直接上结论:

相同的类就加载顺序为:

(0)同一个ClassLoader实例加载的类不能重复,后面加载的类文件不会生效

(1)在web-inf/classes中的类优先于jar包中的加载,此时web-inf/classes中的类生效

(2)不同的jar包都有相同类,则为先加载的jar中的类生效。不同jar加载顺序依赖java -classpath参数指定的顺序。若未指定顺序,则依赖文件系统,可能随机,可能是英文字典顺序。

如何查询已运行的程序加载的类:
启动时可以添加java -verbose:class参数打印加载类信息;在线可使用arthas,attach到运行的程序进行在线调试。


类加载相关基础知识:https://blog.csdn.net/huihuidage/article/details/107193406


下面部分内容转自https://www.cnblogs.com/goodAndyxublog/p/12424734.html,是关于在线调试jvm的工具arthas(阿尔萨斯)资料,可以作为入门,关于arthas具体使用请参考下面两篇文章:

简单入门:https://juejin.cn/post/6844903896347836429

官方文档:https://arthas.aliyun.com/doc/

上篇文章中,小黑哥分析 Maven 依赖冲突分为两类:

  • 项目同一依赖应用,存在多版本,每个版本同一个类,可能存在差异。
  • 项目不同依赖应用,存在包名,类名完全一样的类。

第二种情况,往往是这个场景,本地/测试环境运行的都是好好的,上线之后测试就是不行。

这其实与 JVM 类加载有关,本地/测试环境加载正确类,而生产环节加载错的类,为什么会这样?

主要有两个原因:

  • 同一个类只会被加载器加载一次
  • 不同环境,类的加载顺序不同

同一个类只会被加载器加载一次

JVM 类加载具有缓存机制,每个类加载的时候首先检查一遍,类是否被当前类加载器加载。若未被加载,先交给其父类加载器加载,父类加载器不能加载,才会交给当前类加载器。

当前类加载器加载完成之后,将会将其缓存起来。

类加载的核心源码位于 ClassLoader#loadClass

① 处将会检查ClassLoader#findLoadedClass 最终将会调用 ClassLoader#findLoadedClass0,这是一个 native 方法,最终将会根据类名加类加载器为键值查找缓存。

每个类加载器负责的加载范围都不一样:

  • BootstrapClassLoader 引导类加载加载最核心的类库,如 $JAVA_HOME/jre/lib/
  • ExtClassLoader 扩展类加载器负责加载$JAVA_HOME/jre/lib/ext下的一些扩展类
  • AppClassLoader 应用类加载器将加载 classpath 指定的类。

我们运行的应用依赖的各种类,一般将会由 AppClassLoader 记载,同名类被加载后,下次碰到就不会再被加载。

画外音:利用缓存加快查询速度

不同环境,类的加载顺序不同

Java 可以使用 -classpath 参数指定依赖类所在位置。

类的加载顺序可以通过以下方式指定:

java -classpath a.jar:b.jar:c.jar xx.xx.Main

上面这种方式,类加载首先会从 a.jar 中查找相关类,找不到才会继续往后查找。所以可以通过这种方式可以指定使用哪个 jar 包内同名类。

但是这种方式有点繁琐,如果依赖 100 个 jar 包,需要全部写上去。

所以生产环境可以使用使用 shell 命令将 jar 拼接起来:

LIB_DIR=lib
LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print "'$LIB_DIR'/"$0}'|tr "\n" ":"`

另外 java 支持通配符的写法:

java -classpath './*' xx.xx.Main

这种方式的加载顺序将会受到底层系统文件加载顺序影响。

复现依赖冲突

假设我们现在应用依赖如下:

A 应用依赖 B、C,且 B,C 中存在同包同名类 org.example.App,代码如下:

如果指定 jar 包顺序启动应用:

# A,B,C 放置同一文件夹下
java -classpath A-1.0-SNAPSHOT.jar:B-1.0-SNAPSHOT.jar:C-1.0-SNAPSHOT.jar org.example.ClassA

日志输出如下:

改变 B ,C 顺序:

类加载器的类的查找顺序将会通过 classpath 指定顺序从前往后查找。

如果使用通配符启动:

java -classpath './*' org.example.ClassA

这种情况 jvm 到底加载那个类就成了薛定谔的了,运行之前无法确定加载类来自哪个 jar 包。

使用 verbose:class 打印加载类

我们可以在 jvm 启动脚本加入如下参数 -verbose:class,然后重启,日志里会打印出每个类的加载信息。

java -verbose:class -classpath './*' xx.xx.Main

日志输出如下:

通过这种方式可以看到加载类来源于哪个Jar包。

不过这种方式需要重启应用,对生产系统来说,影响还是比较大,不太优雅。

Arthas 查到来源类

阿里开源项目 Arthas sc 命令可以用来查找加载类的信息。。

sc 命令是 Search-Class 简写,这个命令能搜索出已经加载到 JVM 中的 Class 信息,支持参数如下表格所示。

程序启动之后,启动 arthas,进入 A 应用。(tangtao_xp注释,这个地方是启动arthas后attach到已启动的jvm程序)

运行如下命令:

sc -d org.example.App

输出结果如下 :

code-source 显示当前查找类 org.example.App 来自的 C。

另外我们可以 jad 命令反编译类,在线查看源码。

总结

这篇文章主要解释应用中存在多个同名类,环境不同,类加载不同的原因。接着介绍了两种快速查找运行应用依赖类来源的方法。

当定位到了冲突类的来源,我们可以显示指定 classpath jar 包的顺序,指定类加载的顺序。但这只是暂时解决问题。本质上依赖冲突的问题,还是需要深层次排除的。

关于Java加载类顺序的问题相关推荐

  1. java 加载类java_深入研究Java类加载机制

    深入研究Java类加载机制 类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指导开发者采取更有效的措施配合程序执行. 研究类加载机制的第二个目的是让程序能动态的控制类加载,比 ...

  2. 28 Java类的加载机制、什么是类的加载、类的生命周期、加载:查找并加载类的二进制数据、连接、初始化、类加载器、双亲委派模型、自定义类加载器

    28Java类的加载机制 28.1.什么是类的加载 28.2.类的生命周期 28.2.1.加载:查找并加载类的二进制数据 28.2.2.连接 28.2.3.初始化 28.3.类加载器 28.4.类的加 ...

  3. 【Java 虚拟机原理】Java 类中的类加载初始化细节 ( 只使用类中的常量时加载类不会执行到 ‘初始化‘ 阶段 )

    文章目录 一.类加载初始化时机 二.常量加载示例 三.数组加载示例 一.类加载初始化时机 类加载时机 : Java 程序执行时 , 并不是一开始将所有的字节码文件都加载到内存中 , 而是用到时才进行加 ...

  4. Java运行时动态加载类之ClassLoader加载class及其依赖jar包

    需求场景是:通过ClassLoader动态加载外部class文件,class文件又依赖某个具体jar包,需要动态加载jar包,采用URLClassLoader. 1.xml配置文件 <?xml ...

  5. Java运行时动态加载类之ClassLoader

    需求场景:动态加载类ClassLoaderd,在xml文件中配置加载类名称和方法,: 一.准备 1)在D:\\tmp\\目录下配置a.xml文件: <?xml version="1.0 ...

  6. Java运行时动态加载类之Class.forName

    需求场景:通过Class.forName加载类并创建实例. 1.接口类: package cn.fjs;public interface IC {public void action(); } 2.实 ...

  7. Java运行时动态加载类之URLClassLoader

    需求场景:通过URLClassLoader从jar文件中加载类并创建实例,可实现运行时动态加载 1.要加载的jar: 1)接口类IC package cn.fjs;public interface I ...

  8. java虚拟机加载类_java虚拟机之类加载机制(一)

    一.首先先说一下java虚拟机的生命周期: 1.执行了system.exit(): 2.程序正常执行结束: 3.程序在执行过程中遇到了异常或错误而终止: 4.由于操作系统出现错误而导致java虚拟机进 ...

  9. java加载并运行虚拟机_《深入理解Java虚拟机》- Java虚拟机是如何加载Java类的?...

    Java虚拟机是如何加载Java类的?  这个问题也就是面试常问到的Java类加载机制.在年初面试百战之后,菜鸟喜鹊也是能把这流程倒背如流啊!但是,也只是字面上的背诵,根本就是像上学时背书考试一样. ...

最新文章

  1. (转)使用异步 I/O 大大提高应用程序的性能
  2. 全球最神秘的9大葡萄园...
  3. Redis工作笔记-Hash类型
  4. 线性表【01】线性表的类型定义
  5. scrapy_redis项目配置
  6. AngularJs中,如何在render完成之后,执行Js脚本
  7. linux7.3的域名配置表,Centos7.3 安装部署Nginx并配置https的方法步骤
  8. 实现本地svn目录同步时,服务器的相应目录保持自动同步
  9. Fusion APP-简单网页转app制作教程
  10. 如何导出微信好友?通讯录好友保存
  11. 网络加密流量的相关研究
  12. 数据结构期末复习速成
  13. 硬盘安装win10,笔者教你如何一步步从硬盘安装win10系统
  14. 无需花生壳,阿里云解析实现内网穿透
  15. 华为摄像头(海思3519A/3516D)开发环境nfs共享目录
  16. Shell 脚本 — 多行注释、开启子/不开启子进程执行、转义带颜色输出、读取键盘输入、输入输出重定向、单双引号、命令替换、读取变量、系统变量、正则过滤、算术运算、一行多条命令、字符串比较
  17. 王兴在这个被互联网遗忘的网站上,留下了一万多条碎碎念。
  18. JavaScript 浏览器中执行
  19. OR值的意义和计算公式,和95% CI
  20. 安卓笔记之ViewPager页卡

热门文章

  1. JOJ2237 Hero Ranklist
  2. 基于朴素贝叶斯算法的脱贫攻坚小数据分类算法
  3. Centos下堡垒机Jumpserver V3.0环境部署
  4. ffmpeg安装及使用教程
  5. 正数计时器 和 倒数定时器
  6. 使用递归算法,打印出某个目录下所有子目录和文件
  7. 我国核电具备规模化发展的条件
  8. 011-不知道名字并不妨碍我们买买买
  9. 单片机学习 4-动态数码管显示
  10. 文本的换行与包裹 之我都忘了我写过 break-word