是否收集了未使用的JPA实体垃圾,为什么?

时间:2014-11-28 11:52:30

标签: java memory orm garbage-collection profiling

构建一个使用API​​从Web获取数据的Spring应用程序,我多次碰到OutOfMemoryError: GC overhead limit exceeded。在一些分析会议之后,我开始质疑我的模型,这是这样的:

@Entity
class A {
  @Id
  private Integer id;
  private String name;

  @OneToMany
  private Set<B> b1;

  @OneToMany
  private Set<B> b2;
}

@Entity
Class B {
  @Id
  private Integer id;

  @ManyToOne
  private A a1;

  @ManyToOne
  private A a2;
}

分配了一个CrudRepository来管理这些实体(JPA + EclipseLink)。实体加载是默认的,在这种情况下意味着急切的AFAIK。

该程序尝试执行以下操作:

// populates the set with 2500 A instances.
Set<A> aCollection = fetchAFromWebAPI();
for (A a : aCollection) {
  // populates b1 and b2 of each A with a 100 of B instances
  fetchBFromWebAPI(a);
  aRepository.save(a);
}

在此过程结束时,将有500k个B实例,但由于OutOfMemoryError: GC overhead limit exceeded它永远不会到达终点。现在我可以添加更多内存,但我想了解为什么所有这些实例都不是垃圾回收?将A保存到数据库并忘记它。这是因为A实例在其b1或b2中有B实例,而这些实例又引用A实例吗?

我做的另一个观察是,当数据库中没有数据时,该过程第一次运行得非常顺畅。

此模型或此流程是否存在根本性问题?

2 个答案:

答案 0 :(得分:3)

JPA事务具有事务中使用的所有实体的关联会话缓存。通过保存实体,您可以在该会话缓存中引入更多实例。在您的情况下,我建议使用EntityManager.clear()每个n实体 - 将持久化实体与会话分离,并使其可用于垃圾回收。

如果您想了解更多有关JPA实体生命周期的信息,可以参考例如

http://www.objectdb.com/java/jpa/persistence/managed

编辑: 此外,BatScream的答案也是正确的:您似乎在每个仍然被该集引用的迭代中累积越来越多的数据。您可能需要考虑从集合中删除已处理的实例。

答案 1 :(得分:2)

每次迭代后,集合aCollection都会继续增长。每个A实例将在每个循环后填充200个B实例条目。因此你的堆空间被吃掉了。

在此期间垃圾收集器运行时,集合A中的所有aCollection实例始终可以访问,因为您没有从集合中删除刚刚保存的A

为避免这种情况,您可以使用Set Iterator安全地从集合中删除刚处理过的A实例。