我们的项目中存在性能问题,这似乎源自Hibernate使用类加载器的方式(至少部分)。这是在Java线程转储中发现的,这是在我们内部环境的高负载测试期间获得的。转储的JVM是运行应用程序的Weblogic托管服务器的JVM,并且在监视仪表板显示占用线程和待处理用户请求时进行转储。
示例:的
"[ACTIVE] ExecuteThread: '126' for queue: 'weblogic.kernel.Default (self-tuning)'" daemon prio=10 tid=0x00007f2fe9486000 nid=0x663b waiting for monitor entry [0x00007f2faeae6000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.ClassLoader.loadClass(ClassLoader.java:405)
- waiting to lock <0x000000078c0d76b0> (a weblogic.utils.classloaders.GenericClassLoader)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at weblogic.utils.classloaders.GenericClassLoader.loadClass(GenericClassLoader.java:178)
at org.hibernate.internal.util.ReflectHelper.classForName(ReflectHelper.java:187)
at org.hibernate.internal.util.ReflectHelper.getConstantValue(ReflectHelper.java:278)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl$JavaConstantConverter.handleDotStructure(QueryTranslatorImpl.java:592)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl$JavaConstantConverter.visit(QueryTranslatorImpl.java:587)
我们在这些线程转储(使用Samurai / TDA)中看到的是,似乎有很多线程在等待类加载器上的锁。这个由WLS提供的类加载器似乎是同步的 - 这解释了锁定/阻塞线程模式......
似乎Hibernate使用类加载器来计算查询中的表达式。所以我不确定类加载器调用实际上是否加载了任何新类。
问题是对类加载器的调用次数似乎一直在进行...有时我观察到多达30%的线程总数(~30-40 +我们的130)等着获得一个类加载器锁!
- &GT;当大量线程试图为高用户负载(即许多Hibernate查询)提供服务时,似乎WLS类加载器的同步会导致非常高的内部开销。
现在,这个同步的类加载器问题似乎是限制我们应用程序吞吐量的主要原因,导致在高负载下性能下降。如果我们扩展CPU /内存或各种特定于WLS的池(如EJB / JDBC连接/ ......),问题也不会消失 - 因为它特定于我们运行应用程序的整个JVM。
我非常感谢您对此主题的意见。
P.S。
Google似乎表明我们不是第一个遇到此问题的人(例如this mailing list question或this Oracle Support Question),但对此问题没有真正的解决方案/解释。
答案 0 :(得分:3)
问题在于,应用程序员认为Class.forName()
和Classloader.loadClass()
是new Object()
之类的廉价操作。应用程序服务器认为它们是一种罕见的启动操作。
对于使用条件的hibernate或动态生成的JPQL可以触发此锁争用 http://dimovelev.blogspot.dk/2015/02/performance-pitfalls-hibernate-criteria.html
答案 1 :(得分:2)
Hibernate Jira多次报道此问题:
Class.forName
不是免费的。根据底层应用程序服务器,它可能会导致锁争用,如Dimo Velev's article所示。 Pierre-Hugues Charbonneau在这个主题上有一个similar article,这也很精彩。
如this article中所述,HHH-4959 Jira问题已在Hibernate 5.2.6中得到修复,这应该不再是问题。
答案 2 :(得分:0)
jdk1.6 java.lang.ClassLoader的loadClass是同步的,
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
jdk1.7使每个className的锁定更加有效
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{
synchronized (getClassLoadingLock(name))