Hibernate,SessionFactoryObjectFactory和OutOfMemoryError:java堆空间

Tob*_*obb 7 memory-leaks hibernate sessionfactory out-of-memory

在我工作的地方,我们遇到了JVM在我们的一个应用程序中耗尽堆空间的问题.我已经做了一些搜索导致这种情况的原因,包括使用分析器查看堆转储,但现在我几乎陷入困境.

首先,关于有问题的系统:它是一个使用Spring和Hibernate的Java应用程序来保存有关组织的记录.该系统由一组Web服务客户端组成,用于从负责此类数据的政府机构中检索有关组织的数据.此外,系统使用此类数据保留本地数据库,充当Web服务调用的缓存,以便第一次请求有关组织的信息,将其保存在本地关系数据库中,并将其用于检索以下请求的数据.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.SessionFactoryImpl和一个String.HashMap-entry依次包含相同的三个对象,一个HashMap-entry,一个SessionFactoryImpl和一个String,这个结构重复了很多次.该SessionFactoryImpls包含多个对象,最显着的org.hibernate.persister.entity.SingleTableEntityPersister,里面包含了一些字符串和HashMaps这样的.一些字符串引用域对象中的变量,一些字符串包含sql语句.

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

然后,我试着找到原因,但这要困难得多.作为由Hibernate加载的对象将生活,只要他们的会话的生活,我试图改变会议分别处理的方式,通过更换调用Spring的HibernateDaoSupport#getSession(),对HibernateDaoSupport#getSessionFactory().getCurrentSession().这对问题没有任何影响.我也尝试添加对...的调用getCurrentSession().flush()以及.clear()一些Dao方法的finally块,也没有任何附加效果.(Dao方法都注释了@Transactional,这应该意味着会话应该只在@Transactional-method中存活,并且对方法的连续调用应该在调用getCurrentSession()(?)时获得不同的会话)

所以,现在我在提出其他领域进行检查时几乎陷入困境.有没有人有一个想法或一些关于在哪里寻找和寻找什么的指针?

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

编辑:

我想我实际上已经解决了这个问题:

事实证明,在webservice-classes中处理对其他对象的依赖是一个问题.这是通过ClassPathXmlApplicationContext(...)在webservice类的构造函数中调用new来解决的.这导致为每个请求(或至少每个会话)加载了很多对象,这些对象没有再次卸载(主要是Hibernate SessionFactoryImpl).我已经改变了webservice-classes,所以他们注入了他们的依赖,并形成了我迄今为止使用的探查器,多个SessionFactoryImpl对象的问题已经解决了.

我认为从GlassFish 2.x升级到GlassFish 3.x可能会使问题恶化,可能在如何实例化webservice-classes方面存在一些差异.

Tob*_*obb 5

我不妨在答案中添加解决这个问题的方法,而不仅仅是问题本身:

这里的罪魁祸首是如何在各种对象中加载spring-beans,最明显的是在webservice类中.这是通过电话完成的

新的ClassPathXmlApplicationContext(...)

在各个webservice-classes中.这样做有加载对象的令人讨厌的副作用,避免被垃圾收集(我想因为它们被一些Spring的内部引用).似乎glassfish版本的变化对webservice-objects的实例化做了些什么,导致调用更多对new的调用,因此更多的垃圾对象占用内存,直到它填满并崩溃.

该问题的解决方案是将呼叫转移到

新的ClassPathXmlApplicationContext(...)

使用静态工厂模式进入另一个类,如下所示:

public class ContextHolder {
    private static ClassPathXmlApplicationContext context;

    public static getSpringContext() {
        if (context == null) {
            context = new ClassPathXmlApplicationContext("applicationContext.xml");
        }
        return context;
    }
}
Run Code Online (Sandbox Code Playgroud)

并在webservice-classes中调用它,而不是新增的ClassPathXmlApplicationContext.

更新:

ClassPathXmlApplicationContextCloseable/ Autocloseable,try-with-resource另一种可能性是:

try (final ClassPathXmlApplicationContext applicationContext =
             new ClassPathXmlApplicationContext("applicationContext.xml")) {
    //do stuff
}
Run Code Online (Sandbox Code Playgroud)