未使用的 JPA 实体是不是被垃圾回收?为啥?
Posted
技术标签:
【中文标题】未使用的 JPA 实体是不是被垃圾回收?为啥?【英文标题】:Are JPA entities that are not in use garbage collected and why?未使用的 JPA 实体是否被垃圾回收?为什么? 【发布时间】:2014-11-28 11:52:30 【问题描述】:使用我多次碰到OutOfMemoryError: GC overhead limit exceeded
的 API 构建一个从 Web 获取数据的 Spring 应用程序。在一些分析会议之后,我开始质疑我的模型,这是这样的:
@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 实例吗?
我的另一个观察结果是,当数据库中没有数据时,该过程第一次运行得更加顺畅。
这个模型或这个过程有什么根本错误吗?
【问题讨论】:
【参考方案1】:JPA 事务具有事务中使用的所有实体的关联会话缓存。通过保存您的实体,您可以不断将更多实例引入该会话缓存。在您的情况下,我建议每个 n
实体都使用 EntityManager.clear()
- 这会将持久化的实体从会话中分离出来,并使它们可用于垃圾收集。
如果您想了解更多关于 JPA 实体的生命周期的信息,可以参考例如
http://www.objectdb.com/java/jpa/persistence/managed
编辑: 此外,BatScream 的答案也是正确的:您似乎在每次迭代中积累了越来越多的数据,这些数据仍然被集合引用。您可能需要考虑从集合中删除已处理的实例。
【讨论】:
啊,这是一个困难的情况:你的组合答案是完全正确的答案。【参考方案2】:集合aCollection
在每次迭代后不断增长。在每个循环之后,A
的每个实例都将填充 200 个 B
实例条目。因此你的堆空间被吃光了。
当垃圾收集器在此期间运行时,集合aCollection
中的所有A
实例始终可访问,因为您不会从集合中删除刚刚保存的A
。
为避免这种情况,您可以使用 Set Iterator
从集合中安全地删除刚刚处理的 A
实例。
【讨论】:
以上是关于未使用的 JPA 实体是不是被垃圾回收?为啥?的主要内容,如果未能解决你的问题,请参考以下文章