3

在我们的一个项目中,用户可以将文件附加到他的帐户。我们将这些文件存储在 MS-SQL 数据库中。因此,我们有以下代码:

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

一切都很好,在我们尝试上传 40 MB 文件之前,得到了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,这是由方法java.lang.OutOfMemoryError: Java heap space内部引起的uploadFile()

我做了一个堆转储并在 VisualVM 中查看它。
堆转储

400+ MBchar[]和 100+ MB 的byte[]. 在开始时,我们的应用程序,包括JBoss,正在使用大约 60-65 MB 的堆。那么问题来了,为什么要EntityManager疯狂地消耗堆内存呢?

4

1 回答 1

2

我对您的问题的理解如下。

  • 通过 EntityManager 加载/持久的所有实体都保留在内存中,直到您显式地从它分离实体(通过 EntityManager.detach() 或 EntityManager.clear() 或 EntityManager.close())。所以最好有短命的EntityManagers。

  • 就业务逻辑中发生 RuntimeException 而言,em EntityManager 保持打开状态!你总是想避免这种代码。您可以考虑如下创建和关闭 EntityManager:

    public Customer getBestCustomerOfMonth() {
    EntityManagerFactory emf = ... ;
    EntityManager em = emf.createEntityManager();
    // business logic
    em.close();
    }
    
  • 您可以嵌套用于关闭 EntityManager em.close();的行 在 finally 块内

  • 在企业应用程序服务器之外使用事务时,因为您必须以与 EntityMangers 相同的方式关闭(提交或回滚)事务。为了关闭这些资源(EntityManager 和底层事务),您需要进行额外的嵌套级别并编写类似于此的代码:

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

您可能会认为这种嵌套结构看起来有点乱,但它确实需要在事务的前面。

于 2012-12-06T08:37:47.957 回答