Hibernate、SessionFactoryObjectFactory和OutOfMemoryError:java堆空间

Hibernate、SessionFactoryObjectFactory和OutOfMemoryError:java堆空间,hibernate,memory-leaks,out-of-memory,sessionfactory,Hibernate,Memory Leaks,Out Of Memory,Sessionfactory,在我工作的地方,我们遇到了JVM在我们的一个应用程序中耗尽堆空间的问题。我已经搜索了一些原因,包括使用分析器查看堆转储,但现在我几乎被卡住了 首先,介绍一下所讨论的系统:它是一个Java应用程序,使用Spring和Hibernate来保存有关组织的记录。该系统由一组webservice客户端组成,用于从负责此类数据的政府机构检索有关组织的数据。此外,系统保留一个包含此类数据的本地数据库,作为webservice调用的缓存,以便第一次请求有关组织的信息时,将其保存在本地关系数据库中,并用于检索以下

在我工作的地方,我们遇到了JVM在我们的一个应用程序中耗尽堆空间的问题。我已经搜索了一些原因,包括使用分析器查看堆转储,但现在我几乎被卡住了

首先,介绍一下所讨论的系统:它是一个Java应用程序,使用Spring和Hibernate来保存有关组织的记录。该系统由一组webservice客户端组成,用于从负责此类数据的政府机构检索有关组织的数据。此外,系统保留一个包含此类数据的本地数据库,作为webservice调用的缓存,以便第一次请求有关组织的信息时,将其保存在本地关系数据库中,并用于检索以下请求的数据。Hibernate用于与此数据库通信

如前所述,问题在于一段时间后,应用程序开始崩溃,并出现OutOfMemoryError:java堆空间。我已经查看了使用Eclipse+MAT的堆转储,并确定了罪魁祸首是Hibernate的
SessionFactoryObjectFactory
,它占用了大约85%的分配内存(所有内存都是保留的)。我发现很难准确地确定这里面保存了什么类型的对象。在顶层有Glassfish
WebappClassLoader
,其中包含
org.hibernate.impl.SessionFactoryObjectFactory
。其中包含一个
org.hibernate.util.FastHashMap
,它又包含一个
java.util.HashMap
。它包含许多条目,每个条目都包含一个HashMap条目、一个
org.hibernate.impl.sessionfactorympl
和一个字符串。HashMap条目依次包含相同的三个对象,一个HashMap条目、一个
SessionFactoryImpl
和一个字符串,该结构会重复多次。
sessionfactorympl
s包含许多对象,最显著的是
org.hibernate.persister.entity.SingleTableEntityTyperMaster
,其中包含许多字符串和哈希映射。有些字符串引用域对象中的变量,有些字符串包含sql语句

乍一看,这个对象似乎占用了不必要的内存(转储文件为800MB,其中650MB被
SessionFactoryObjectFactory
占用),因此我启用了对象加载和卸载日志记录,并尝试向系统询问有关组织的数据(通过另一个系统的webservice调用). 我注意到这里有很多关于加载对象的消息,但是很少有关于卸载对象的消息(仅有的消息是卸载库对象)。这让我相信,一旦一个对象(比如一个组织)被加载到内存中,它就永远不会被卸载,这意味着随着时间的推移,系统将耗尽内存。(基于日志中的发现,这是一个公平的假设吗?)

然后,我试着找出原因,但这要困难得多。由于Hibernate加载的对象将与其会话寿命一样长,因此我尝试改变会话的处理方式,方法是将对Spring的
HibernateDaoSupport#getSession()
HibernateDaoSupport#getSessionFactory().getCurrentSession()的调用替换为
HibernateDaoSupport#getSessionFactory()
。这对这个问题没有明显的影响。我还尝试在讨论中的一些Dao方法的最后一个块中添加对…
getCurrentSession().flush()
.clear()
的调用,同样没有明显的效果。(Dao方法都用
@Transactional
注释,这意味着会话应该只在
@Transactional
-方法中处于活动状态,并且在调用
getCurrentSession()
(?)时,对该方法的连续调用应该得到不同的会话)

所以,现在,当涉及到其他需要检查的区域时,我几乎被卡住了。有没有人对去哪里找和找什么有什么想法或建议

堆转储显示有很多
org.hibernate.impl.SessionFactoryImpl
的实例,这是预期的吗?(我本以为应该只有一个SessionFactory实例,或者几个TOP实例。)

编辑:

我想我已经设法解决了这个问题:

事实证明,在webservice类中处理对其他对象的依赖关系的方式是问题所在。通过在webservice类的构造函数中调用new
ClassPathXmlApplicationContext(…)
解决了这个问题。这导致为每个请求(或至少每个会话)加载了大量对象,但没有再次卸载(主要是Hibernate的
SessionFactoryImpl
)。我已经更改了webservice类,因此它们将注入它们的依赖关系,并且形成了我在使用探查器时看到的形式。到目前为止,多个
SessionFactoryImpl
-对象的问题已经解决


我认为从GlassFish 2.x升级到GlassFish 3.x可能会使问题更加严重,可能是Web服务类的实例化方式有所不同。

我还可以在回答中添加此问题的解决方案,而不仅仅是问题本身:

这里的罪魁祸首是如何在各种对象中加载Springbean,尤其是在webservice类中。这是通过打电话来完成的

新的ClassPathXmlApplicationContext(…)

在各个Web服务类中。这样做的副作用是加载的对象避免被垃圾收集(我猜是因为它们被Spring的一些内部构件引用)。glassfish版本的更改似乎对webservice对象的实例化产生了影响,导致调用更多的新对象,从而导致更多的垃圾对象占用内存,直到它填满并崩溃
public class ContextHolder {
    private static ClassPathXmlApplicationContext context;

    public static getSpringContext() {
        if (context == null) {
            context = new ClassPathXmlApplicationContext("applicationContext.xml");
        }
        return context;
    }
}
try (final ClassPathXmlApplicationContext applicationContext =
             new ClassPathXmlApplicationContext("applicationContext.xml")) {
    //do stuff
}