【问题标题】:JPA EntityManager big memory problemsJPA EntityManager 大内存问题
【发布时间】:2012-04-18 06:10:38
【问题描述】:

我在使用 Spring、Hibernate 和 JPA 的 Web 应用程序时遇到了一些问题。问题是非常高的内存消耗,它随着时间的推移而增加,而且似乎永远不会减少。它们很可能源于对 EntityManager 的错误使用。我已经四处寻找,但我还没有找到确定的东西。

我们使用的 DAO 都扩展了以下 GenericDAO,其中我们唯一的 EntityManager 被注入:

public abstract class GenericDAOImpl<E extends AbstractEntity<P>, P> implements
    GenericDAO<E, P> {

@PersistenceContext
@Autowired
private EntityManager entityManager;
    [...]

使用通用 DAO 是因为它具有通过 ID 获取实体的方法等,这在所有约 40 个 DAO 中实现起来会很痛苦。

EntityManager 通过以下方式配置为 Spring bean:

<bean class="org.springframework.orm.jpa.JpaTransactionManager"
    id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven mode="aspectj"
    transaction-manager="transactionManager" />
<bean
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    id="entityManagerFactory">
    <property name="persistenceUnitName" value="persistenceUnit" />
    <property name="dataSource" ref="dataSource" />
</bean>
<bean id="entityManager" factory-bean="entityManagerFactory"
    factory-method="createEntityManager" scope="singleton" />

我认为最大的问题是使用这个共享的 EntityManager 来处理所有事情。在服务类中,我们将 @Transactional 注释用于需要事务的方法。这会从我读取的内容中自动刷新 EntityManager,但与清除不同,所以我猜对象仍在内存中。

我们注意到每天在数据库中每次自动导入数据后内存都会增加(大约 7 个文件,每个文件 25k 行,其中创建了很多链接对象)。但在正常运行期间,当检索大量数据时(假设一次请求 100-200 个对象)。

有人知道我可以如何改善目前的情况(因为现在情况有点糟糕......)?

编辑:已在已部署的应用程序上运行分析器,发现如下:

One instance of "org.hibernate.impl.SessionFactoryImpl" loaded by "org.apache.catalina.loader.WebappClassLoader @ 0xc3217298" occupies 15,256,880 (20.57%) bytes. The memory is accumulated in one instance of "org.hibernate.impl.SessionFactoryImpl" loaded by "org.apache.catalina.loader.WebappClassLoader @ 0xc3217298".

这大概是EntityManager没有清除吧?

【问题讨论】:

  • 我在使用PersistenceContext 时遇到了与springhibernate 相同的问题。你解决泄漏了吗?

标签: java spring hibernate jpa entitymanager


【解决方案1】:

我倾向于同意您的评估。 EntityManagers aren't really designed to be used as singletons。刷新 EntityManager 不会从内存中清除任何内容,它只会将实体与数据库同步。

可能发生的情况是 EntityManager 保持对持久性上下文中所有对象的引用,而您永远不会关闭上下文。 (This guy 有类似的问题。)清除它确实会删除从 EntityManager 到您的实体的所有引用,但是,如果您发现自己经常需要调用 clear(),您可能应该重新评估您如何使用 EntityManager。如果您只是想避免 LazyInitializationExceptions,请考虑 Spring* 中的 OpenSessionInViewFilter。这允许您延迟加载实体,同时仍然让 Spring 管理 bean 的生命周期。 bean 的生命周期管理是 Spring 框架的一大优势,因此您需要确保覆盖该行为是您真正想要的。

确实在某些情况下您需要一个长寿命的 EntityManager,但这些情况相对较少,需要大量了解才能正确实施。

*注意:OpenSessionInView 需要非常小心以避免N+1 problemsome call Open Session in View an AntiPattern 真是个大问题。谨慎使用。

编辑

此外,您也不需要使用 @Autowired 注释 @PersistenceContext 元素。 @PersistenceContext 自行接线。

【讨论】:

    【解决方案2】:

    您应该从上面的private EntityManager entityManager; 中删除@Autowired 注释,并从您的上下文定义文件中删除entityManager bean 定义。此外,如果您不使用 &lt;context:annotation-config/&gt;&lt;context:component-scan/&gt; XML 标签,则必须在您的上下文中定义 PersistenceAnnotationBeanPostProcessor bean。

    【讨论】:

      【解决方案3】:

      不符合 JEE 的应用服务器,你不应该使用@Autowired/@PersistenceContext private EntityManager entityManager;

      你应该做的是这样的:

      class SomeClass {
         @PersistenceUnit private EntityManagerFactory emf;
      
         public void myMethod() {
            EntityManager em = null;
            try {
               em = emf.createEntityManager();
               // do work with em
            } 
         } catch (SomeExceptions e) {
            // do rollbacks, logs, whatever if needed
         } finally {
            if (em != null && em.isOpen()) {
              // close this sucker
              em.clear();
              em.close();
            }
         }
      } 
      

      一些注意事项:

      • 这适用于带有 Spring + Hibernate 的非完整 JEE 应用服务器
      • 我用 JDK 1.7 和 1.8 测试过,在泄漏方面没有区别。
      • 常规的 Apache Tomcat 不是真正的 JEE 应用服务器(但是TomEE 是)
      • List of Java EE Compliant App Servers

      【讨论】:

      • 定期运行 em.clear() 正是大幅提高我的处理速度的原因
      猜你喜欢
      • 1970-01-01
      • 2011-10-09
      • 2013-07-25
      • 2011-01-22
      • 1970-01-01
      • 2012-02-11
      • 2013-09-01
      • 1970-01-01
      • 2011-05-03
      相关资源
      最近更新 更多