Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EntityManager memory consumption

In one of our projects user can attach file to his account. We store these files in the MS-SQL database. So, we have the following piece of code:

@Entity
public class File extends AbstractEntity {

    @Lob
    @Basic
    private byte[] data;

    @Nullable
    public byte[] getData() {
        return data;
    }

    public void setData(byte[] data) {
        this.data = data;
    }

    public File() {
    }

    public File(byte[] data) {
        this.data = data;
    }
}

public class SomeBean {

    @PersistenceContext
    protected EntityManager em;

    public Long uploadFile(@NotNull byte[] data) {
        final PhysicalFile physicalFile = new PhysicalFile();
        physicalFile.setData(data);
        em.persist(physicalFile);
        return physicalFile.getId();
    }
}

And all was nice and pretty, before we tried to upload 40 MB file, and got java.lang.RuntimeException: javax.transaction.RollbackException: [com.arjuna.ats.internal.jta.transaction.arjunacore.commitwhenaborted] [com.arjuna.ats.internal.jta.transaction.arjunacore.commitwhenaborted] Can't commit because the transaction is in aborted state, which was caused by java.lang.OutOfMemoryError: Java heap space inside the uploadFile() method.

I made a heap dump and looked at it in VisualVM.
Heap Dump

400+ MB of char[] and 100+ MB of byte[]. On the start our application, including JBoss, was using something about 60-65 MB of heap. So, the question is, why EntityManager consumes heap memory like crazy?

like image 366
aga Avatar asked Sep 19 '26 09:09

aga


1 Answers

My understaning to your issue as follows.

  • All the entities that load/persist through an EntityManager stay in memory until you explicitly detach the entities from it (via EntityManager.detach() or EntityManager.clear() or EntityManager.close()). So it's better to have short-lived EntityManagers.

  • As far as a RuntimeException occurs in the business logic, the em EntityManager remains open! You'll always want to avoid this sort of code. you can think of creating and closing the EntityManager as follows:

    public Customer getBestCustomerOfMonth() {
    EntityManagerFactory emf = ... ;
    EntityManager em = emf.createEntityManager();
    // business logic
    em.close();
    }
    
  • You can nest the line for closing the EntityManager em.close(); inside a finally block

  • when using transactions outside an enterprise application server because you'll have to close (commit or rollback) the transaction in the same way you do for EntityMangers. In order for these resources (both EntityManager and underlying transaction) to be closed you'll need to make an additional level of nesting and write your code similar to this one:

         public Customer updateCustomer(Customer cust) {
    
           EntityManagerFactory emf = ... ;   EntityManager em =
         emf.createEntityManager();   try {
         EntityTransaction t = em.getTransaction();
         try {
           t.begin();  
           // business logic to update the customer
           em.merge(cust);
           t.commit();
         } finally {
           if (t.isActive()) t.rollback();
         }   } finally {
         em.close();
           }       
        }
    

You may think this nested structure could looks like a bit of a mess, but it is really needed in precence of transactions.

like image 198
Harish Raj Avatar answered Sep 20 '26 22:09

Harish Raj



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!