【问题标题】:Debugging Memory leak - org.hibernate.engine.StatefulPersistenceContext调试内存泄漏 - org.hibernate.engine.StatefulPersistenceContext
【发布时间】:2018-11-22 04:04:09
【问题描述】:

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

在使用 Eclipse MAT 分析几个 heapdump 后,发现大部分内存是在 org.hibernate.engine.StatefulPersistenceContext -> org.hibernate.util.IdentityMap -> java.util.LinkedHashMap 的一个实例中积累的

泄密嫌疑人说

线程 java.lang.Thread @ 0x84427e10 ... : 29 保持本地 总大小为 1,582,637,976 (95.04%) 字节的变量。

内存在“java.util.LinkedHashMap”的一个实例中累积 由“”加载。

在 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 RepositoryJpaSpecificationExecutor。两者都输入到它将处理的域类中。

API 活动方法有注解@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)

【问题讨论】:

  • 发布您的休眠配置以及您的初始化方式SessionFactory
  • 同时打开调试日志并发布该日志。
  • @Amogh 正如问题中提到的,由于我们使用的是 Hibernate 和 Spring Data JPA,因此 SessionFactory 没有显式初始化或在代码中使用。

标签: java hibernate memory-leaks spring-data-jpa sessionfactory


【解决方案1】:

根据您的描述,这是我期望的:

Hibernate(实际上通常是 JPA)在会话的生命周期内保留对它加载或保存的所有实体的引用。

在典型的 Web 应用程序设置中,这不是问题,因为。一个新会话从每个请求开始,并在请求完成后关闭,并且不涉及那么多实体。

但是对于您的应用程序,会话似乎不断增长。 我可以想象以下原因:

  • 某些东西一直在打开的会话中运行,而从未关闭。可能是批处理作业或定期运行的计划作业。

  • Hibernate 的配置方式是重复使用同一个会话而无需关闭它。

为了找到罪魁祸首,启用打开和关闭会话的日志记录。从https://hibernate.atlassian.net/browse/HHH-2425 来看,org.hibernate.impl.SessionImpl 应该是正确的日志类别,您可能需要跟踪级别的日志记录。

现在测试对服务器的各种请求,看看是否有任何会话打开但未关闭。

【讨论】:

  • 该服务有多个API与不同的表交互并查看IdentityMap的值,它属于特定的API。服务中的所有 API 都遵循相同的模式(使用 Spring Data Repository 和 JpaSpecificationExecutor),并且无法确定为什么会发生这种情况。另外,这个API的流量是正常的,没有突然增加。没有定期运行的计划/批处理作业。但是,由于使用情况,有时流量很高。
  • 另外,要在会话打开/关闭时添加日志,是否需要在存储库文件中显式访问来自 EntityManager 的 Session obj?因为当前代码中没有使用 SessionFactory。你能详细解释一下吗?
  • 由于该方法是用@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)注解的,它应该为每个请求刷新会话缓存,对吧?
  • 通常这是通过 Hibernate 的标准配置和 Spring Transaction 机制来处理的。 Spring 关闭事务,这也关闭了会话。所以问题是:这是否按预期发生。如果日志显示不是,我们可以查看配置以找出原因。
  • SUPPORTS 表示它将加入当前事务或在没有当前可用的情况下打开自己的事务。刷新可能发生在不同的时间(在选择之前、在提交之前、根据请求),但只会将更改写入数据库。它不会清空会话。结束事务也应该关闭会话,所以它应该收集更多的资源。
【解决方案2】:

该问题包含有关创建某些 bean 的信息。但问题不在于那里。问题出在您的代码中,您在哪里使用这些 bean。

请检查您的代码。可能您正在循环加载项目。并且循环被一个事务包裹起来。

Hibernate 会创建巨大的中间对象,并且它不会在事务完成(提交/回滚)之前清理这些对象。

【讨论】:

    猜你喜欢
    • 2010-12-01
    • 2021-01-10
    • 2010-11-23
    • 1970-01-01
    • 2013-09-28
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多