Search code examples
javahibernatejpamemory-leakspersistence

Huge memory leak during Hibernate entity persisting, why?


I am using Hibernate with JPA EntityManager, not Hibernate sessions. Most people seem to recommend using a new EntityManager for every transaction, so that is what I'm doing.

So, I create 10000 new entities in a row. The heap usage is about 90 MB on the first iteration and 5 GB on the last one.

java memory leak

This is my iteration code:

    Session session = Core.getDatabase().createEntityManager().unwrap(Session.class);
    Query query = session.getNamedQuery("Account.getAllIds");
    query.setReadOnly(true);
    query.setFetchSize(100);
    ScrollableResults accounts = query.scroll(ScrollMode.FORWARD_ONLY);

    EntityManager entityManager = Core.getDatabase().createEntityManager();
    entityManager.getTransaction().begin();

    while (accounts.next()) {

        int id = (int) accounts.get(0);
        Account account = entityManager.getReference(Account.class, id);

        Core.getDatabase().persist(new AccountTest(account));
        changes++;

    }

    entityManager.close();
    accounts.close();
    session.close();

And my persistence function:

public class Database
{
    private final EntityManagerFactory entityManagerFactory;

    public Database(String host, String database, String user, String password)
    {
        Map<String, Object> properties = new HashMap<String, Object>();
        properties.put("javax.persistence.jdbc.url", "jdbc:mysql://" + host + ":3306/" + database);
        properties.put("javax.persistence.jdbc.user", user);
        properties.put("javax.persistence.jdbc.password", password);

        entityManagerFactory = Persistence.createEntityManagerFactory("default", properties);
    }

    public EntityManager createEntityManager()
    {
        return entityManagerFactory.createEntityManager();
    }

    public void persist(Object... objects)
    {
        EntityManager entityManager = createEntityManager();
        entityManager.getTransaction().begin();

        for (Object object : objects) {
            entityManager.persist(object);
        }

        entityManager.getTransaction().commit();
        entityManager.clear();
        entityManager.close();
    }
}

So I iterate over all account entities (10k, but using Hibernate Session because of the scrolling functionality) and want to create a new AccountTest for every account.


Solution

  • The problem is you hold the first EntityManager opened the whole time, while only clearing and closing another EntityManager that's only used for persisting one item.

    At the end, you have all the Account entities loaded in the first EntityManager.