我正在尝试处理重量级元素(图像)的集合。收集的大小在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();
}
答案 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
}
}
顺便说一句,我尝试了无状态解决方案,该解决方案为我带来了这种异常,但我没有解决(可能可以改善此答案)Exception details is here
org.hibernate.SessionException: collections cannot be fetched by a stateless session
因此,我将sleep(delay)作为其后台过程进行了调整,并且由于服务器上的资源不足而需要很长时间,因此我必须冷却cpu;进行午夜工作(无高峰时间)。