加载Java实体时内存不足

加载Java实体时内存不足,java,jpa,jakarta-ee,ejb-3.0,Java,Jpa,Jakarta Ee,Ejb 3.0,我有一个可怕的问题,希望有一个非常简单的答案。执行基本操作时,内存不足 如果我有这样的代码: MyEntity myEntity; for (Object id: someIdList) { myEntity = find(id); // do something basic with myEntity } find()方法是与EntityManager相关的标准方法: public MyEntity find(Object id) { return em.find(my

我有一个可怕的问题,希望有一个非常简单的答案。执行基本操作时,内存不足

如果我有这样的代码:

MyEntity myEntity;
for (Object id: someIdList) {
   myEntity = find(id); 
   // do something basic with myEntity
}
find()方法是与EntityManager相关的标准方法:

public MyEntity find(Object id) {
    return em.find(mycorp.ejb.entity.MyEntity.class, id);
}
这段代码在几周前就开始工作了,如果数据库中的项目较少,它就可以正常工作。我面临的最终错误是:

java.lang.OutOfMemoryError:超出GC开销限制

oracle toplink调用一些oracle jdbc方法时出现了例外


循环之所以存在,是因为当存在大量记录时,EJBQL(如“将MyEntity中的对象(o)选择为o”)将使应用程序服务器过载。

问题是,如果执行循环,您只需查询一个实体,但它仍然在EntityManager中被引用。你要么

  • 清除()实体管理器,或
  • 从中删除实体(忘记如何调用该实体的函数)
如果可能的话,最好将实体管理器设置为只读,因为这样可以阻止JPA在内存中保存每个对象的副本,只是为了检测在数据库发生flush()时是否可能发生更改

这段代码在几周前就开始工作了,如果数据库中的项目较少,它就可以正常工作。我面临的结果错误是:
java.lang.OutOfMemoryError:超出了GC开销限制

这里没有什么令人惊讶的。由
em.find()
加载的实体被放置并保存在持久性上下文(内存中)中,以跟踪更改,因此,如果您在没有预防措施的情况下批量加载了太多的实体,您只会爆炸内存并获得OOME

如果确实需要对所有实体执行某些操作,首先需要调用
flush()
,将所有更改推送到数据库,然后调用
clear()
定期清除持久上下文并释放内存:

int i = 0;
for (Object id: someReallyBigIdList) {
    myEntity = find(id); 
    // do something basic with myEntity
    if ( i % 20 == 0 ) { //20, same as the JDBC batch size
        //flush a batch of DML operations and release memory:
        em.flush();
        em.clear();
    }
    i++;
}

调用
clear()
会导致所有托管实体分离。对尚未刷新到数据库的实体所做的更改将不会持久化因此需要
flush()
首先进行更改

执行em.clear()并不是我想要的答案。。。但我会记住的。这个问题的问题是,它过去是有效的。。。使用相同级别的数据库条目!这就引出了另一个问题-如果我有一个循环,我可以定期刷新/清除。。但是,当应用程序只是长时间运行时会发生什么情况呢?EntityManager应该管理它管理的内容!
EntityManager
的生命周期通常很短(常见的模式是每个请求使用EntityManager)。我建议你读书。如果不遵循此模式,则必须处理“内存处理”。
EntityManager
将实体保存在内存中以跟踪更改,这就是JPA的工作方式,您不能合理地期望
EntityManager
能够加载整个数据库或刷新更改并分离实体。我只是在Hibernate中遇到一个问题,将实体设置为只读并不意味着它停止持久性上下文以在持久性上下文中保存副本,它仍然会保存,因为后续加载/查找将从持久性上下文而不是数据库中获取副本。