在我们的一个项目中,用户可以将文件附加到他的帐户。我们将这些文件存储在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多MB char[]
和100 + MB byte[]
。在开始时,我们的应用程序(包括JBoss
)正在使用大约60-65 MB的堆。所以,问题是,为什么EntityManager
像疯了一样消耗堆内存?
答案 0 :(得分:1)
我对你的问题的理解如下。
通过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(); 的行 块
在企业应用程序服务器外部使用事务时 因为你必须关闭(提交或回滚)交易 与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();
}
}
您可能认为这种嵌套结构看起来有点混乱,但在事务发生之前确实需要它。