处理大量元素时,Hibernate内存不足异常

时间:2014-01-01 14:13:25

标签: java performance hibernate out-of-memory data-processing

我正在尝试处理重量级元素(图像)的集合。收集的大小在8000-50000个条目之间变化。但由于某些原因,在处理1800-1900条目之后,我的程序属于java.lang.OutOfMemoryError:Java堆空间。

在我的理解中,每当我调用session.getTransaction()时,commit()程序应该释放堆内存,但看起来它永远不会发生。 我做错了什么?这是代码:

private static void loadImages( LoadStrategy loadStrategy ) throws IOException {
    log.info( "Loading images for: " + loadStrategy.getPageType() );

    Session session = sessionFactory.openSession();
    session.setFlushMode( FlushMode.COMMIT );
    Query query = session.createQuery( "from PageRaw where pageType = :pageType and pageStatus = :pageStatus and sessionId = 1" );
    query.setString( "pageStatus", PageStatus.SUCCESS.name() );
    query.setString( "pageType", loadStrategy.getPageType().name() );
    query.setMaxResults( 50 );

    List<PageRaw> pages;
    int resultNum = 0;

    do {

        session.getTransaction().begin();

        log.info( "Get pages statring form " + resultNum + " position" );
        query.setFirstResult( resultNum );
        resultNum += 50;
        pages = query.list();
        log.info( "Found " + pages.size() + " pages" );


        for (PageRaw pr : pages ) {
            Set<String> imageUrls = new HashSet<>();
            for ( UrlLocator imageUrlLocator : loadStrategy.getImageUrlLocators() ) {
                imageUrls.addAll(
                        imageUrlLocator.locateUrls( StringConvector.toString( pr.getSourceHtml() ) )
                );
            }

            removeDeletedImageRaws( pr.getImages(), imageUrls );
            loadNewImageRaws( pr.getImages(), imageUrls );
        }

        session.getTransaction().commit();

    } while ( pages.size() > 0 );

    session.close();
}

4 个答案:

答案 0 :(得分:5)

您已将刷新清除混淆:

  • 刷新会话执行针对数据库的所有挂起语句(它将内存状态与数据库状态同步);

  • 清除会话会清除会话(第1级)缓存,从而释放内存。

因此,您需要刷新清除会话以恢复占用的内存。

除此之外,您必须禁用二级缓存。否则,即使在清除会话后,所有(或大部分)对象仍将保持可用。

答案 1 :(得分:4)

我不知道为什么你认为提交事务会释放堆内存。运行垃圾收集就是这样做的。

如果您的perm gen耗尽,可能会发生OOM错误。

简单的答案是在启动JVM时更改最小和最大堆大小和perm gen大小,看它是否消失。

我建议像VisualVM一样获取一个分析器,并查看在运行时消耗内存的内容。它应该很容易修复。

我猜你一次尝试过大的块。将其分解成更小的部分,看看是否有帮助。

答案 2 :(得分:0)

尝试使用session.clear() “完全清除会话。逐出所有已加载的实例并取消所有挂起的保存,更新和删除。不要关闭打开的迭代器或ScrollableResults的实例”

答案 3 :(得分:0)

本文解决了我的问题

    Session session = sessionFactory.getCurrentSession();
      ScrollableResults scrollableResults = session.createQuery("from DemoEntity").scroll(ScrollMode.FORWARD_ONLY);
      int count = 0;
      while (scrollableResults.next()) {
       if (++count > 0 && count % 100 == 0) {
        System.out.println("Fetched " + count + " entities");
       }
       DemoEntity demoEntity = (DemoEntity) scrollableResults.get()[0];
       //Process and write result
       session.evict(demoEntity);//important to add this
      }
     }

bulk fetching hibernate

  1. 使用休眠的ScrollableResult
  2. 使用逐出

顺便说一句,我尝试了无状态解决方案,该解决方案为我带来了这种异常,但我没有解决(可能可以改善此答案)Exception details is here

org.hibernate.SessionException: collections cannot be fetched by a stateless session

因此,我将sleep(delay)作为其后台过程进行了调整,并且由于服务器上的资源不足而需要很长时间,因此我必须冷却cpu;进行午夜工作(无高峰时间)。