8

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

 3 years ago
source link: https://www.heapdump.cn/article/2697019
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
杭盖3周前

作为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倍左右。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK