在我工作的地方,我们的一个应用程序中遇到了 JVM 堆空间不足的问题。我已经对此进行了一些搜索,包括使用分析器查看堆转储,但现在我几乎陷入困境。
首先,关于所讨论的系统:它是一个 Java 应用程序,使用 Spring 和 Hibernate 来保存有关组织的记录。该系统由一组 Web 服务客户端组成,用于从负责此类数据的政府机构检索有关组织的数据。此外,系统将这些数据保存在本地数据库中,作为 Web 服务调用的缓存,以便第一次请求有关组织的信息时,将其保存在本地关系数据库中,用于检索以下请求的数据。Hibernate 用于与该数据库进行通信。
如前所述,问题是一段时间后,应用程序开始崩溃,并出现 OutOfMemoryError: java heap space。我查看了使用 Eclipse+MAT 的堆转储,并将罪魁祸首确定为 Hibernate 的SessionFactoryObjectFactory
,占用了大约 85% 的分配内存(全部保留内存)。我发现很难确定其中保存了哪些类型的对象。在顶层有 Glassfish WebappClassLoader
,其中包含org.hibernate.impl.SessionFactoryObjectFactory
. 包含在其中的是 a org.hibernate.util.FastHashMap
,而后者又包含 a java.util.HashMap
。这包含许多条目,每个条目都包含一个 HashMap 条目、一个org.hibernate.impl.SessionFactoryImpl
和一个字符串。HashMap-entry 又包含相同的三个对象,一个 HashMap-entry,一个SessionFactoryImpl
和一个字符串,这个结构会重复多次。SessionFactoryImpl
s 包含许多对象,最值得注意的是,org.hibernate.persister.entity.SingleTableEntityPersister
它包含许多 Strings 和 HashMaps。一些字符串引用域对象中的变量,一些包含 sql 语句。
乍一看,这个对象似乎占用了不必要的内存(转储文件为 800MB,其中 650MB 被 占用SessionFactoryObjectFactory
),所以我启用了对象加载和卸载的日志记录,并尝试向系统询问有关组织(通过来自另一个系统的 Web 服务调用)。我注意到这里有很多关于加载对象的消息,但很少有关于卸载对象的消息(唯一的消息是卸载库对象)。这让我相信一旦一个对象(比如一个组织)被加载到内存中,它就永远不会被卸载,这意味着随着时间的推移,系统将耗尽内存。(根据日志中发现的内容,这是一个公平的假设吗?)
然后,我试图找到造成这种情况的原因,但这要困难得多。由于 Hibernate 加载的对象将与它们的会话寿命一样长,我尝试通过将 Spring 的调用替换HibernateDaoSupport#getSession()
为HibernateDaoSupport#getSessionFactory().getCurrentSession()
. 这对问题没有明显的影响。我还尝试添加对 ... 的调用,getCurrentSession().flush()
并且.clear()
在一些有问题的道方法的 finally-block 中,也没有明显的效果。(Dao-methods都是用 注释的@Transactional
,这应该意味着一个会话应该只在@Transactional
-method内是活跃的,并且对该方法的连续调用应该在调用时获得不同的会话getCurrentSession()
(?))
所以,现在我在提出其他要检查的领域时几乎被卡住了。有没有人有关于在哪里寻找和寻找什么的想法或一些指示?
堆转储显示有很多实例org.hibernate.impl.SessionFactoryImpl
,这是否符合预期?(我原以为应该只有一个 SessionFactory 实例,或者几个顶部。)
编辑:
我想我实际上已经设法解决了这个问题:
事实证明,在 webservice-classes 中处理对其他对象的依赖是问题所在。这是通过ClassPathXmlApplicationContext(...)
在 web 服务类的构造函数中调用 new 来解决的。这导致为每个请求(或至少每个会话)加载大量对象,而这些对象没有再次卸载(主要是 Hibernate 的SessionFactoryImpl
)。我已经更改了 webservice-classes,因此它们注入了它们的依赖项,并形成了我迄今为止使用分析器看到的内容,多个SessionFactoryImpl
-objects 的问题已经解决。
我认为从 GlassFish 2.x 升级到 GlassFish 3.x 可能会加剧问题,这可能是关于如何实例化 webservice-classes 的一些差异。