Java 在处理大量元素集合时发生休眠内存不足异常

Java 在处理大量元素集合时发生休眠内存不足异常,java,performance,hibernate,out-of-memory,data-processing,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

我正在尝试处理重元素(图像)的收集。收集的数量在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();
}
private static void loadImages(LoadStrategy-LoadStrategy)引发IOException{
log.info(“为:+loadStrategy.getPageType()加载图像”);
Session Session=sessionFactory.openSession();
session.setFlushMode(FlushMode.COMMIT);
Query Query=session.createQuery(“来自PageRaw,其中pageType=:pageType和pageStatus=:pageStatus和sessionId=1”);
query.setString(“pageStatus”,pageStatus.SUCCESS.name());
query.setString(“pageType”,loadStrategy.getPageType().name());
query.setMaxResults(50);
列表页;
int resultNum=0;
做{
session.getTransaction().begin();
log.info(“获取页面列表”+resultNum+“位置”);
query.setFirstResult(resultNum);
结果num+=50;
pages=query.list();
log.info(“找到”+页面.size()+页面”);
用于(PageRaw pr:pages){
Set imageUrls=newhashset();
对于(UrlLocator-imageUrlLocator:loadStrategy.getImageUrlLocators()){
imageURL.addAll(
imageUrlLocator.locateUrls(StringConverctor.toString(pr.getSourceHtml()))
);
}
移除删除的ImageRaws(pr.getImages(),imageUrls);
loadNewImageRaws(pr.getImages(),imageUrls);
}
session.getTransaction().commit();
}while(pages.size()>0);
session.close();
}

我不知道为什么您认为提交事务可以释放堆内存。运行垃圾收集可以做到这一点

如果perm gen用完,可能会发生OOM错误

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

我建议使用一个分析器,比如VisualVM,看看运行时消耗内存的是什么。它应该很容易修复

我猜你是想一下子投入太多。将其分成小块,看看是否有帮助。

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

您将刷新与清除混淆了:

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

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

因此,您需要刷新和清除会话才能恢复占用的内存


除此之外,还必须禁用二级缓存。否则,即使在清除会话之后,所有(或大部分)对象仍然可以访问。

本文解决了我的问题

    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
      }
     }

  • 使用hibernate ScrollableResult
  • 使用逐出
  • 顺便说一句我尝试了无柄解决方案,它给了我这个异常,我不知道如何解决(也许你可以改进这个答案)


    所以我调整了睡眠(延迟)作为后台进程,在服务器资源很低的情况下,我需要很长时间来冷却cpu;午夜工作(没有高峰时间)。

    我怀疑是因为上课。所以我添加了这一行session.setFlushMode(FlushMode.COMMIT)。但看起来我错了。我以前从未使用过探查器,我想是时候开始了。)对于记录,有效地释放内存的不是垃圾收集,而是对象变得不可访问。这是OP假设在事务提交(隐式刷新)时会发生的情况。如何禁用二级缓存?
    session.execute()
    会有帮助吗?
    org.hibernate.SessionException: collections cannot be fetched by a stateless session