我们的项目中有一个性能问题,似乎(至少部分)源于 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)等待获得类加载器锁!
--> 当大量线程试图为高用户负载(即许多 Hibernate 查询)提供服务时,WLS 类加载器的同步似乎会导致非常高的内部开销。
这种行为是正常的还是我们做错了什么?
现在这个同步类加载器问题似乎是限制我们应用程序吞吐量的主要原因,导致在重负载下性能下降。此外,如果我们扩展 CPU/内存或各种 WLS 特定池(如 EJB/JDBC 连接/...),问题也不会消失——因为它特定于我们运行应用程序的整个 JVM。
非常感谢您就此主题提出意见。
附言
谷歌似乎表明我们不是第一个遇到这个问题的人(例如 this mailing list question 或 this Oracle Support Question ),但是这个问题没有真正的解决方案/解释。
最佳答案
问题是应用程序员认为Class.forName()
和 Classloader.loadClass()
像 new Object()
这样的廉价操作将是。并且应用服务器认为它们是罕见的启动操作。
对于使用标准或动态生成的 JPQL 的 hibernate 可以触发此锁争用 http://dimovelev.blogspot.dk/2015/02/performance-pitfalls-hibernate-criteria.html
关于java - 来自 Hibernate 的同步类加载器调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26608973/