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

我正在尝试处理重量级元素(图像)的集合。 收集的大小在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 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 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(); } 

你把冲洗清算混淆了:

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

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

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

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

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

如果您的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

  1. 使用hibernate ScrollableResult
  2. 使用逐出

顺便说一句,我尝试了无定位解决方案它给了我这个例外我没有怎么解决(可能你可以改进这个答案) exception细节在这里

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

所以我调整了睡眠(延迟)作为它的背景过程而且很长时间在服务器上的资源很少我必须冷却CPU; 午夜工作(没有高峰时间)。