JPA EntityManager大内存问题

时间:2012-04-04 14:04:47

标签: java spring hibernate jpa entitymanager

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

我们正在使用DAO,这些DAO都扩展了以下GenericDAO,其中注入了我们的ONLY 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,但确实与清除不同,所以我猜这些对象仍在内存中。

我们注意到每天在数据库中自动导入数据后内存增加(大约每个25k行的7个文件,其中创建了很多链接对象)。但是在正常运行期间,当检索大量数据时(让我们说一次请求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未被清除?

3 个答案:

答案 0 :(得分:3)

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

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

确实有些情况下你需要一个长期存在的EntityManager,但这些情况相对较少,需要很多理解才能正确实现。

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

<强> 修改

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

答案 1 :(得分:1)

非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

答案 2 :(得分:0)

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