【问题标题】:JPA / Hibernate Out of Memory ExceptionJPA /休眠内存不足异常
【发布时间】:2017-04-13 08:29:31
【问题描述】:

我最近将一个使用本机 Hibernate 实体和会话管理的项目转移到 Spring,并带有注释驱动的依赖注入和事务管理。

我有一个这样的实体结构:

@Entity
@Table(name = "PARENT",uniqueConstraints=@UniqueConstraint(columnNames={"parentName"}))
public class Parent implements Serializable, {
    static final long serialVersionUID = 1L;

    @Id
    private int id;


    @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<Child> child = new HashSet<Child>();
}



@Entity
@Table(name = "CHILD", uniqueConstraints = @UniqueConstraint(columnNames = {
        "childName", "parent_id" }))
@SequenceGenerator(name = "CHILD_SEQ", allocationSize = 1, sequenceName = "CHILD_SEQ")
public class Child implements Serializable {

    static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CHILD_SEQ")
    private int id;
    private String childName = ""; //$NON-NLS-1$

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Parent parent;
}

这就是发生的事情。

  • 我得到了所有包含子对象单独列表的父对象。这工作很快,我在调试器的内存中有完整的对象树,我可以看到每个子对象都正确加载。

    public List<Parent> getParents() {
        return em.createQuery("from Parent",Parent.class).getResultList();
    }
    
  • 然后我尝试获取每个 Child 对象,应用程序冻结我的电脑变慢,几个小时后我得到 Out on memory 异常。

    public List<Child> getChildren() {
        return em.createQuery("from Child",Child.class).getResultList();
    }
    

我查看了生成的 SQL,对于第一种方法,它似乎在逻辑上将调用分解为单独的对象集,而在几秒钟内,它似乎构建了一个我无法真正遵循的怪物大小的查询,并且查询似乎没有返回,虽然我不能确定。

我无法理解的是为什么对父项的查询工作得如此之快,并且给了我每个对象,但子查询却中断了。

【问题讨论】:

  • 能分享一下持久化逻辑吗
  • 格式有问题。
  • 尝试使用 em.persist 而不是合并,因为持久化对象不会被管理,如果这不起作用,请共享调用 saveChild() 的代码
  • FetchType.EAGER 有什么具体原因吗?除非有很好的理由,否则应该是FetchType.LAZY,因为EAGER 有加载非常大的对象图的风险,这可能导致OutOfMemory 异常。
  • 那么您在数据库中真正有多少孩子?

标签: java spring hibernate jpa persistence


【解决方案1】:

当您从父母开始时,Hibernate 可以读取所有父母(简单)并加入子表(同样,简单)。每个孩子都有一个父母,这是我们加入的那个,所以这就足够了。虽然整个数据库被读入内存,但它很小。

另一方面,当您从孩子开始时,Hibernate 必须首先获取每个孩子的父母。这意味着与父表的一个联接。但是,那些父母有孩子,所以我们必须再次加入孩子表才能找到他们。

您的课程不完整,缺少父姓名,并且您提到了其他收藏。如果其他集合也很急切,则必须使用第一个子级别和第二个子级别的连接来获取它们,每个集合再添加两个连接。这会导致非常复杂的查询!

【讨论】:

  • 延迟获取和手动控制何时初始化这些集合有效。不过,我最初的问题是,当从使用带有手动控制的本机休眠转移到 JPA / Spring 时,即使所有数据结构都相同并且所有函数都在执行等效操作,问题还是出现了。因此,看起来原生 hibernate 调用实际上可以比 JPA 的实现更好地解决这些循环,这让我感到惊讶。
  • Eager 指定 JPA 必须在加载父级时加载子级,但不是如何加载。我相信 Hibernate 默认使用连接(获取),这就是给您带来问题的原因。我认为 EclipseLink 会为每个 Parent 做一个 sub-qeury,所以它不会有问题。如果您需要控制行为,Hibernate 允许您指定 FetchModestackoverflow.com/questions/32984799/…
【解决方案2】:

这可能是内存泄漏造成的。 您可以使用增加堆空间。

-Xms&lt;size&gt; set initial Java heap size

-Xmx&lt;size&gt; set maximum Java heap size

http://javarevisited.blogspot.com.ng/2011/09/javalangoutofmemoryerror-permgen-space.html

但是,很高兴知道 java.lang.OutOfMemoryError: Java heap space 的原因可能是什么。使用分析器可以帮助您弄清楚是什么占用了堆大小。 如果您使用的是 Netbeans,则可以使用 https://profiler.netbeans.org/ 或找到与您的 IDE 兼容的分析器。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2018-09-20
  • 2011-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-05
相关资源
最近更新 更多