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

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

3年前
16079611

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

点赞收藏
电梯战神
请先登录,查看6条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

从 Linux 内核角度探秘 JDK MappedByteBuffer

从 Linux 内核角度探秘 JDK MappedByteBuffer

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

11
6