类加载分析方案以及预防办法
来源:互联网 发布:mysql innodb重建索引 编辑:程序博客网 时间:2024/06/10 09:33
java类加载中的版本冲突是一个头疼的问题,这里列举几种排查问题以及处理的常用办法
有时候在测试环境下正常的,线上部分机器也是正常,但是有一些机器就会异常,这是啥原因?
因为jar包的加载顺序在不同的机器上面是不同的,一台机器多次重启,基本的顺序不会变,但是新的jar包就会有问题
在不同的机器上,对jar包中类的加载顺序有时候不是完全一致的,例如,在/home/admin/.default/lib 目录下的有个A.jar和b.jar,里面都有个叫SayHello的class,但A的SayHello里的功能(例如他打印的是“去你的”)和b的SayHello功能(例如他打印的是“你好”)不一样,
由于不同机器加载顺序可能不一致,那么就有可能A机器上先加载了A.jar的SayHello类(会打印出“去你的”),而B机器上加载了b.jar的SayHello类(会打印出“你好”)。
这个问题在特定的机器上,其加载顺序基本不变的,因此,一旦测试的时候没问题,那他就很难再重现出此问题,只有部署到线上多台机器上时,问题才可能会暴露出来。
(1)通过maven自带的几个工具来进行分析
1、mvn dependency:list能够列举出项目中的依赖情况
2、mvndependency:tree打印出依赖出(\-代表行尾) 这个在排除依赖的时候非常有用,通过树状结构来查看jar包的加载情况
3、mvndependency:analyzer 这个能够分析项目中的依赖情况。
Used undeclareddependencies 是指那些在项目中直接使用到的,但没有在POM中配置的依赖。要注意一点,这个分析是编译主代码和测试代码需要的依赖,运行时需要的没有打印出来。例如该例中可能项目中的一些类有关于spring-context的Java import声明,但spring-context这个依赖实际是通过传递性依赖进入classpath的,这就意味者潜在的风险。一般来说我们对直接依赖的版本变化会比较清楚,因为那是我们自己直接配置的,但对于传递性依赖的版本变化,就会比较模糊,当这种变化造成构建失败的时候,就很难找到原因。因此我们应当增加这些 Used undeclared dependencies 。
依赖分析还提供了 Unuseddeclared dependencies 供我们参考,这表示那些我们配置了,但并未直接使用的依赖。需要注意的时,对于这些依赖,我们不该直接简单地删除。由于dependency:analyze只分析编译主代码和测试代码使用的依赖,一些执行测试和运行时的依赖它发现不了,因此还需要人工分析。通常情况,Unused declared dependencies 还是能帮助我们发现一些无用的依赖配置。
补充一个maven传递依赖的两个原则
(1)最短路径优先
例如项目A中有这样的依赖关系,A->B->C->X(1.0) A->D->X(2.0) 分别传递依赖了X包的两个版本,
此时X(1.0)的路径为3,X(2.0)的路径为2,maven优先使用2.0)这个包
(2)第一声明者优先
上面这个原则不能解决所有问题,例如X(1.0)和X(2.0)的路径相同,这时候会使用那个包?
在依赖路径长度相同的情况下,POM依赖声明的顺序决定了谁会被解析使用,顺序靠前的那个依赖优先使用。
例如:
A->B->Y(1.0) A->C->Y(2.0)
如果B的依赖声明在C之前,则Y(1.0)版本会被解析使用。
(2)在应用启动的时候通过-verbose来进行分析
这个能够辅助进行一些分析,不过jvm启动参数上面加入-verbose可能会导致jboss挂起,可以使用,但是不要作为常态化来使用
1、在JVM的启动参数里面加入“-verbose”,这样会在日志里面显示类加载的信息;
2、输出JVM启动的时候的信息到特定的文件(可选)-XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=<path>
通过脚本抓取日志里面从WEB-INF中加载的jar包
grep'^\[Loaded.*/WEB-INF/lib.*' jboss_stdout.log | awk '{print $4 }' | cut -d'/'-f10 | cut -d']' -f1 |sort -u
(3)在编译阶段就预警出问题来,
在编译后,可以通过两种方式来进行
1、在WEB-INF目录下的lib中,解压jar包,然后递归比较类文件名称,如果有一个类名称相同的文件出现在两个jar包中,则有可能会发生冲突,提示出来即可
2、遍历maven插件,在编译的时候进行校验。
大体过程可以分为两步,第一步通过maven自带的API获取所有的jar包文件
第二步,遍历文件,将所有的类信息放到map或者set中,然后进行比较,如果文件曾经出现过,则提示有可能冲突。
private MavenProject project; private boolean failOnWarning; @SuppressWarnings("unchecked") public void execute() throws MojoExecutionException, MojoFailureException { Set<Artifact> artifacts = project.getArtifacts(); HashMap<String, Artifact> classAndArtifactMap = new HashMap<String, Artifact>(); boolean hasWarnings = false; for (Artifact artifact : artifacts) { ZipInputStream zis = null; try { zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(artifact.getFile()))); ZipEntry entry; while ((entry = zis.getNextEntry()) != null) { if (entry.isDirectory() || !entry.getName().endsWith(".class")) { continue; } if (classAndArtifactMap.containsKey(entry.getName())) { hasWarnings = true; getLog().warn("DUPLICATED CLASS FOUND! " + entry.getName() + "\r\n\t" + artifact + "\r\n\t" + classAndArtifactMap.get(entry.getName())); break; } classAndArtifactMap.put(entry.getName(), artifact); } } catch (Exception e) { throw new MojoExecutionException("Unknown errors...", e); } finally { IOUtils.closeQuietly(zis); } } if (failOnWarning && hasWarnings) { throw new MojoFailureException("PLEASE CHECK ABOVE WARNINGS!!! There is duplicated classes found in your dependencies."); } }
第一种方式在排查问题的时候非常有用,第二种方式不推荐,极力推荐在编译阶段就把有可能存在类冲突的类查出来
- 类加载分析方案以及预防办法
- Docker的安全问题以及一些预防方案
- PHP SQL 注入攻击的技术实现以及预防办法
- PHP SQL 注入攻击的技术实现以及预防办法
- PHP SQL 注入攻击的技术实现以及预防办法
- PHP SQL 注入攻击的技术实现以及预防办法
- PHP SQL 注入攻击的技术实现以及预防办法
- PHP SQL 注入攻击的技术实现以及预防办法
- PHP SQL 注入攻击的技术实现以及预防办法
- 预防颈椎病七个办法
- CSRF攻击以及预防
- DDOS攻击以及预防
- ListView加载速度/性能优化方案分析
- ListView加载速度/性能优化方案分析
- ListView加载速度/性能优化方案分析
- ListView加载速度/性能优化方案分析
- “狙击波”病毒预防及处理办法
- 骨关节炎常见的预防办法有哪些
- 【原】GC的默认方式
- mysql中文字符乱码
- 【原】有效的时间管理-读书笔记
- 【译】分析java线程(analyze java thread)
- StringBuilder的toString方法
- 类加载分析方案以及预防办法
- threadPoolExecutor代码
- 20130709 【南华大学 ACM】 新生赛第二场 【B. Dating With Girls】
- 【转】主流分词器对比
- 策略模式初探
- 空间范围内检索方案
- NGX 信号处理流程
- 【转】最简单也最复杂的设计模式
- ios 坑爹的 APNS 回调方法didReceiveRemoteNotification