4

I am encountering some problems with a web app that uses Spring, Hibernate and JPA. The problems are very high memory consumption which increases over time and never seems to decrease. They most likely stem from an incorrect usage of the EntityManager. I have searched around but I haven't found something for sure yet.

We are using DAOs which all extend the following GenericDAO where our ONLY EntityManager is injected:

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

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

The generic DAO is used because it has methods to get entities by ID and so on which would be a pain to implement in all ~40 DAOs.

The EntityManager is configured as a Spring bean in the following way:

<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" />

The biggest problem I think is using this shared EntityManager for everything. In the services classes, we are using the @Transactional annotation for methods which require a transaction. This flushes the EntityManager automatically from what I read, but does is different from clearing, so I guess the objects are still in memory.

We noticed an increase in memory after each automatic import of data in the DB which happens every day (~7 files of 25k lines each, where a lot of linked objects are created). But also during normal functioning, when retrieving lots of data (let's say 100-200 objects at a time for a request).

Anyone has any idea how I could improve the current situation (because it's kind of bad at this point...)?

Edit: Have run a profiler on the deployed app and this is what it found:

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".

This is probably the EntityManager is not cleared?

AndaP
  • 1,258
  • 8
  • 22
  • 40
  • I'm having the same issue with `spring` and `hibernate` while using `PersistenceContext`. did you solve the leak? – oak Feb 11 '14 at 13:18

3 Answers3

3

I'm inclined to agree with your assessment. EntityManagers aren't really designed to be used as singletons. Flushing the EntityManager doesn't clear anything from memory, it only synchronizes entities with the database.

What is likely happening is the EntityManager is keeping reference to all of the objects in the persistence context and you're never closing the context. (This guy had a similar issue.) Clearing it will indeed remove all references from EntityManager to your entities, however, you should probably re-evaluate how you use your EntityManager in general if you find yourself constantly having to call clear(). If you are just wanting to avoid LazyInitializationExceptions, consider the OpenSessionInViewFilter from Spring*. This allows you to lazily load entities while still letting Spring manage the lifecycle of your beans. Lifecycle management of your beans is one of the great advantages of the Spring Framework, so you need to make sure that overriding that behavior is really what you want.

There are indeed some cases where you want a long-lived EntityManager, but those cases are relatively few and require a great deal of understanding to implement properly.

*NOTE: OpenSessionInView requires great care to avoid the N+1 problem. It's such a big issue that some call Open Session in View an AntiPattern. Use with caution.

Edit

Also, you don't need to annotate @PersistenceContext elements with @Autowired as well. The @PersistenceContext does the wiring itself.

Community
  • 1
  • 1
Tim Pote
  • 24,528
  • 6
  • 58
  • 63
1

The non JEE compliant application server, you should not be using @Autowired/@PersistenceContext private EntityManager entityManager;!

What you should be doing is something like this:

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();
      }
   }
} 

Some notes:

  • This applies to Non Full JEE app server with Spring + Hibernate
  • I've tested it with JDK 1.7 and 1.8, no difference in terms of leaks.
  • Regular Apache Tomcat is not true JEE app server (TomEE is however)
  • List of Java EE Compliant App Servers
Jenya G
  • 474
  • 3
  • 8
0

You should delete @Autowired annotation from above private EntityManager entityManager; and remove entityManager bean definition from your context definition file. Also, if you don't use <context:annotation-config/> and <context:component-scan/> XML tags you must define PersistenceAnnotationBeanPostProcessor bean in your context.

Tomasz Szymulewski
  • 1,723
  • 20
  • 22