调试内存泄漏 - org.hibernate.engine.StatefulPersistenceContext

5

有一个连接到Oracle数据库读取数据的服务,它使用Hibernate-3.6SpringData-JPA-1.10.x。主机频繁生成堆转储,导致内存不足。

通过使用Eclipse MAT分析了一些堆转储后,发现大多数内存积累在一个org.hibernate.engine.StatefulPersistenceContext -> org.hibernate.util.IdentityMap -> java.util.LinkedHashMap实例中。

enter image description here 而泄漏嫌疑人说

The thread java.lang.Thread @ 0x84427e10 ... : 29 keeps local variables with total size 1,582,637,976 (95.04%) bytes.

The memory is accumulated in one instance of "java.util.LinkedHashMap" loaded by "".

在StackOverflow上搜索,它说SessionFactory应该是单例的,并且在每次调用清除缓存之前应调用session.flush()和session.clear()。但是代码中没有明确初始化或使用SessionFactory

这里是什么导致了内存泄漏(看起来每个查询的结果都被缓存并且没有被清除),如何解决?

有关Spring Data配置的更多信息:

TransactionManager被初始化为:

<tx:annotation-driven mode='proxy' proxy-target-class='true' />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
   ....
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" depends-on="...">
   ....
</bean>

为了与表交互,需要声明一个接口并扩展Spring Data的Repository和JpaSpecificationExecutor。这两个类型都应与该接口所处理的领域类(domain class)相一致。
API活动方法带有注释@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)。

请发布您的Hibernate配置以及如何初始化“SessionFactory”。 - Amogh
同时打开调试日志并发布该日志。 - Amogh
正如问题中所提到的,由于我们使用了Hibernate和Spring Data JPA,因此代码中没有显式初始化或使用SessionFactory。 - Saravanan
2个回答

4
根据您的描述,我认为情况如下:
Hibernate(实际上是JPA)在会话期间对其加载或保存的所有实体保持引用。在典型的Web应用程序设置中,这不是问题,因为每个请求都会启动一个新会话,并在请求完成后关闭它,并且不涉及太多实体。
但是对于您的应用程序,似乎会话不断增长。我可以想象以下原因:
- 某些内容一直在打开的会话中运行,而从未关闭。可能是批处理作业或定期运行的作业之类的东西。 - Hibernate 配置为重复使用相同的会话而从未关闭它。
为了找到罪魁祸首,请启用打开和关闭会话的日志记录。根据https://hibernate.atlassian.net/browse/HHH-2425org.hibernate.impl.SessionImpl 应该是正确的日志类别,您可能需要跟踪级别日志记录。
现在测试您的服务器各种请求,查看是否有会话被打开但未关闭。

该服务有多个API与不同的表进行交互,并查看IdentityMap的值,最终落入特定的API。服务中的所有API都遵循相同的模式(使用Spring Data Repository和JpaSpecificationExecutor),但无法确定为什么会发生这种情况。此外,该API的流量正常,没有突然增加。没有定期运行的计划/批处理作业。但是,由于使用情况,有时流量很高。 - Saravanan
由于该方法使用了@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)注解,它应该在每个请求时刷新会话缓存,对吗? - Saravanan
SUPPORTS 表示它将加入当前事务或在没有当前事务的情况下打开自己的事务。Flush 可能在不同的时间发生(在 select 之前,在 commit 之前,按请求),但只写入对数据库的更改。它不清空会话。结束事务也应关闭会话,因此它应该收集更多资源。 - Jens Schauder
你找到解决方案了吗?谢谢。 - Rashmi
@Saravanan,这个问题有什么发现吗? - Jacob
显示剩余6条评论

0
该问题涉及到一些bean的创建信息。但问题并不在此,而是在您的代码中,您是在哪里使用了这些bean。
请检查您的代码。可能您正在循环加载项目,并且该循环被事务包装。
Hibernate会创建大量的中间对象,在事务完成(提交/回滚)之前不会清理这些对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接