性能文章>记一次类加载失败导致线程阻塞问题排查>

记一次类加载失败导致线程阻塞问题排查原创

8月前
1367315

作为PerfMa解决方案管理部门的技术专家,我在工作遇见过很多各种问题导致的性能问题,并参与了为客户的系统进行性能诊断调优的全过程。这一次碰到了一个类加载失败导致的性能问题。用文字记录下了问题的整个发现-排查-分析-优化的过程,排查过程中使用了我司商业化产品——XLand性能分析平台,通过文章主要希望跟大家分享下分析和优化思路以及注意点,有兴趣深入了解的同学可以评论交流。

问题现象:

生产上发现有几个接口响应时间很长,并监控到了线程阻塞的情况

分析过程:

先在测试环境上通过对这几个接口进行压测,尝试复现性能问题,并通过XLand监控。

CPU热点分析:
从CPU热点可以看到业务层的clearLocalSessionWithTradeLogin方法,调用了owner组件中的方法(经了解这是一个实现了配置热修改的组件),最终走到findEditor方法时(需确认是否用到了反射)触发了一个类加载的操作,但没找到该类,加载失败。
1111.png

线程分析:
通过Thread dump的线程视图可以看到有很多Block状态的线程,都是卡在类加载这一步
2222.png

再来看看是持锁线程在做什么?
发现持锁线程的堆栈是和cpu热点中的线程栈一样的,也是在试图加载类,但是没找到类文件,抛出了ClassNotFoundException
3333.png

内存分析:
从内存dump的对象视图搜索ClassNotFoundException对象,发现试图加载的类是java.lang.stringEditor对象
4444.png

分析小结:
综合以上信息看出,业务方法调用了owner组件中的方法,owner组件试图加载StringEditor类,但没找到类文件,抛出了ClassNotFoundException。同时由于多线程情况下,只有一个线程会去加载StringEditor类,其他线程会被阻塞(Block)直至StringEditor加载成功(或者加载失败抛出异常)。但由于缺少StringEditor类文件,因此加载StringEditor类的线程注定会失败,然后又会有其他线程去加载类,然后又失败,周而复始,永远在抛ClassNotFoundException,永远有线程被阻塞(Block)。。。

性能风险点:

1.如果一个类还未被加载到内存中,在多线程调用该类中方法的时候,只有一个线程会去加载该类,其他线程会被阻塞。正常情况下,类加载成功,那么其他线程就不用再阻塞,可以直接该类或对象中的方法。但一旦类加载失败,其他线程还回去视图加载该类,导致永远在抛ClassNotFoundException,永远有线程被阻塞(Block),恶性循环。

2.频繁抛ClassNotFoundException也会产生很多临时对象,导致YGC更频繁,造成更多STW并浪费CPU资源

优化建议:

由于owner组件并非业务强相关,事实上虽然不停在抛ClassNotFoundException,并未导致业务的报错。与开发协商后决定去掉owner组件的调用。

经性能比对测试,不去调用owner组件后,不再抛出ClassNotFoundException,也没有线程阻塞(Block)现象,响应时间大幅下降,TPS成倍增长。相同并发数下,提升最明显的接口TPS增长了10倍左右。

请先登录,再评论

图压缩的啥也看不见了……

8月前
回复 堆堆:

就是,图看的不太清。不过看了还是有种在现场观战的感觉

8月前回复
回复 空无:

比较早的记录了,图都是从word里保存的,的确不够清晰了😷

8月前回复

为你推荐

不起眼,但是足以让你有收获的JVM内存分析案例
分析 这个问题说白了,就是说有些int[]对象不知道是哪里来的,于是我拿他的例子跑了跑,好像还真有这么回事。点该 dump 文件详情,查看相关的 int[] 数组,点该对象的“被引用对象”,发现所
从一起GC血案谈到反射原理
前言 首先回答一下提问者的问题。这主要是由于存在大量反射而产生的临时类加载器和 ASM 临时生成的类,这些类会被保留在 Metaspace,一旦 Metaspace 即将满的时候,就会触发 Fu
关于内存溢出,咱再聊点有意思的?
概述 上篇文章讲了JVM在GC上的一个设计缺陷,揪出一个导致GC慢慢变长的JVM设计缺陷,可能有不少人还是没怎么看明白的,今天准备讲的大家应该都很容易看明白 本文其实很犹豫写不写,因为感觉没有
协助美团kafka团队定位到的一个JVM Crash问题
概述 有挺长一段时间没写技术文章了,正好这两天美团kafka团队有位小伙伴加了我微信,然后咨询了一个JVM crash的问题,大家对crash的问题都比较无奈,因为没有现场,信息量不多,碰到这类问题我
又发现一个导致JVM物理内存消耗大的Bug(已提交Patch)
概述 最近我们公司在帮一个客户查一个JVM的问题(JDK1.8.0_191-b12),发现一个系统老是被OS Kill掉,是内存泄露导致的。在查的过程中,阴差阳错地发现了JVM另外的一个Bug。这个B
JVM实战:优化我的IDEA GC
IDEA是个好东西,可以说是地球上最好的Java开发工具,但是偶尔也会卡顿,仔细想想IDEA也是Java开发的,会不会和GC有关,于是就有了接下来对IDEA的GC进行调优 IDEA默认JVM参数: -
不起眼,但是足以让你收获的JVM内存案例
今天的这个案例我觉得应该会让你涨姿势吧,不管你对JVM有多熟悉,看到这篇文章,应该还是会有点小惊讶的,不过我觉得这个案例我分享出来,是想表达不管多么奇怪的现象请一定要追究下去,会让你慢慢变得强大起来,
如何通过反射获得方法的真实参数名(以及扩展研究)
前段时间,在做一个小的工程时,遇到了需要通过反射获得方法真实参数名的场景,在这里我遇到了一些小小的问题,后来在部门老大的指导下,我解决了这个问题。通过解决这个问题,附带着我了解到了很多新的知识,我觉得