JPA / Hibernate 内存溢出异常

3

我最近将一个使用本地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;
}

Here's what happens.

  • I get all the parent objects which contain individual lists of child objects. This works quickly, I have the full object tree in memory in my debugger I can see every Child object is loaded correctly.

    public List<Parent> getParents() {
        return em.createQuery("from Parent",Parent.class).getResultList();
    }
    
  • I then try and get every Child object and the application freezes my PC slows down and after a few hours I get the Out on memory exception.

    public List<Child> getChildren() {
        return em.createQuery("from Child",Child.class).getResultList();
    }
    
我查看了生成的SQL语句,对于第一种方法,它似乎将调用逻辑上分解为单个对象集,而对于第二种方法,则似乎构造了一个我无法真正理解的庞大查询,该查询似乎没有返回结果,尽管我无法确定。
我无法理解的是为什么查询父级如此迅速并给我每个对象,但子查询却会中断。

你能分享一下持久化逻辑吗? - Amer Qarabsa
尝试使用em.persist而不是merge,因为持久化的对象将不受管理。如果这不起作用,请分享调用saveChild()的代码。 - Amer Qarabsa
1
有使用 FetchType.EAGER 的特别原因吗?除非有非常好的理由,否则应该使用 FetchType.LAZY,因为 EAGER 会有加载非常大的对象图的风险,这可能会导致 OutOfMemory 异常。 - manish
那么在数据库中,你真正拥有多少个子元素? - ThomasRS
是的,这似乎没有做任何事情。尽管如此,我还是不愿意仅仅更改属性来看看是否有所帮助。 - Link19
显示剩余13条评论
2个回答

4
当您从父级开始时,Hibernate可以读取所有父级(简单)并与子表(同样简单)连接。每个子项都有一个确切的父项,这是我们要连接的父项,因此这已经足够了。当整个数据库读入内存时,由于其很小,所以没有问题。
另一方面,当您从子级开始时,Hibernate必须首先为每个子项获取父项。这意味着需要与父表进行一次连接。然而,那些父项有子项,因此我们必须再次连接子表才能找到它们。
您的类不完整,缺少父名称并提及其他集合。如果其他集合也是急切的,它们将必须随着第一级和第二级子项一起使用连接进行提取,每个集合增加两个连接。这将导致非常复杂的查询!

延迟获取和手动控制何时初始化这些集合是可行的。然而,我的原始问题是,当从使用本地Hibernate并进行手动控制转换为JPA / Spring时,即使所有数据结构都相同且所有函数执行等效操作,问题仍会出现。因此,似乎本地Hibernate调用实际上可以比它们的JPA实现更好地解决这些循环,这让我感到惊讶。 - Link19
Eager指定了JPA必须在加载父对象时加载其子对象,但没有指定如何加载。我认为Hibernate默认使用连接(fetch),这就是导致问题的原因。我认为EclipseLink将为每个Parent执行一个子查询,因此不会有任何问题。如果需要控制行为,Hibernate允许您指定FetchMode。https://dev59.com/B1wY5IYBdhLWcg3wH0xC - Klaus Groenbaek

-1
可能是内存泄漏导致了这个问题。 您可以使用以下命令来增加堆空间大小。

-Xms<size> 设置初始Java堆大小

-Xmx<size> 设置最大Java堆大小

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

然而,了解可能导致 java.lang.OutOfMemoryError: Java heap space 的原因是很好的。使用分析器可以帮助您找出是什么在占用堆大小。 如果您正在使用 Netbeans,则可以使用 https://profiler.netbeans.org/ 或查找与您的 IDE 兼容的分析器。


当我调用em.merge()之前出现错误,任何内存问题都应该是Hibernate的问题,而不是我需要或可以解决的问题。 - Link19

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接