一次ClassLoader引发的应用无响应

事情是这样的

在一个平平无奇的上午,收到同事反馈,某个应用突然特别卡,响应时间非常长,于是让他先保存应用运行的快照,用jstack命令导出三次线程信息,每次间隔3到5秒。

拿到线程信息后,用TDA工具打开,话不多话,直接上图

一次ClassLoader引发的应用无响应-LMLPHP

其它两个线程信息也是如此,略去不表。可以看到,有554个线程在等着拿org.eclipse.jetty.webapp.WebAppClassLoader这把锁,WebAppClassLoader是应用中间件jetty用来加载类的。

分析

第一个为什么

原因

项目使用的Controller层由于历史原因,不是使用spring管理的单例,而是每个请求过来,通过Class.forName反射生成的项目大量使用Hibernate和HQL语言,会将查询结果转换成pojo类,会大量调用hibernate的ReflectHelper.classForName反射生成对象实例。

一次ClassLoader引发的应用无响应-LMLPHP

jetty7.6WebappClassLoader是直接在loadClass方法上加synchonized

一次ClassLoader引发的应用无响应-LMLPHP

第二个为什么

原因

如果不加锁,那同一个ClassLoader有可能同时加载多次同名Class,产生多个Class实例,这样会让程序运行产生不可预知的结果。

第三个为什么

为什么加锁要在loadClass上加锁,一类不锁它不香吗

原因

一类一锁是香,只是jdk的开发人员没想到你们能这么去用反射,所以之前就没这么实现,在jdk1.7ClassLoader类才实现了一类一锁

一次ClassLoader引发的应用无响应-LMLPHP

然后jetty9WebappClassLoader实现了按类名加锁

一次ClassLoader引发的应用无响应-LMLPHP

第四个为什么

为什么Hibernate的ReflectHelper.classForName用了这么多反射

原因

人谁无过不过hibernate在5.XX的版本进行了优化

一次ClassLoader引发的应用无响应-LMLPHP

最后的解决方案

由于升级jetty或者hibernate的代价较大,临时重新了jetty7.6的ClassLoader和Hibernate的ReflectHelper.原因都找出来了,其他项目组可以根据自己的具体情况选择不同的解决方案。晚安!


本文分享自微信公众号 - 程序员阿水(gh_124d28263603)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

09-08 05:58