Java 使用hibernate读取大量数据时的OutOfMemory
我需要从数据库导出大量数据。以下是表示我的数据的类:Java 使用hibernate读取大量数据时的OutOfMemory,java,hibernate,orm,out-of-memory,batch-processing,Java,Hibernate,Orm,Out Of Memory,Batch Processing,我需要从数据库导出大量数据。以下是表示我的数据的类: public class Product{ ... @OneToMany @JoinColumn(name = "product_id") @Cascade({SAVE_UPDATE, DELETE_ORPHAN}) List<ProductHtmlSource> htmlSources = new ArrayList<ProductHtmlSource>(); } getProdu
public class Product{
...
@OneToMany
@JoinColumn(name = "product_id")
@Cascade({SAVE_UPDATE, DELETE_ORPHAN})
List<ProductHtmlSource> htmlSources = new ArrayList<ProductHtmlSource>();
}
getProductIterator的代码:
public ScrollableResults getProductIterator(int offset, int limit) {
Session session = getSession(true);
session.setCacheMode(CacheMode.IGNORE);
ScrollableResults iterator = session
.createCriteria(Product.class)
.add(Restrictions.eq("status", Product.Status.DONE))
.setFirstResult(offset)
.setMaxResults(limit)
.scroll(ScrollMode.FORWARD_ONLY);
session.flush();
session.clear();
return iterator;
}
问题是,尽管我在读取每个数据块Product
后清除了会话,但对象在某个地方累积,我遇到了get-OutMemory异常。问题不在于处理代码块,即使没有它,我也会得到内存错误。批处理的大小也不是问题,因为1000个对象很容易放入内存
分析器显示对象在org.hibernate.engine.StatefulPersistenceContext
类中累积
堆栈跟踪:
Caused by: java.lang.OutOfMemoryError: Java heap space
at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:99)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:518)
at java.lang.StringBuffer.append(StringBuffer.java:307)
at org.hibernate.type.TextType.get(TextType.java:41)
at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:163)
at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:154)
at org.hibernate.type.AbstractType.hydrate(AbstractType.java:81)
at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2101)
at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1380)
at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1308)
at org.hibernate.loader.Loader.getRow(Loader.java:1206)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:580)
at org.hibernate.loader.Loader.doQuery(Loader.java:701)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.loadCollection(Loader.java:1994)
at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:36)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:565)
at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:63)
at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1716)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:344)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:109)
at org.hibernate.collection.PersistentBag.size(PersistentBag.java:225)
**at com.rivalwatch.plum.model.Product.getHtmlSource(Product.java:76)
at com.rivalwatch.plum.model.Product.getHtmlSourceText(Product.java:80)
at com.rivalwatch.plum.readers.AbstractDataReader.getData(AbstractDataReader.java:64)**
你能发布异常stacktrace吗? 它可以通过为GC传递合适的JVM选项来解决 我认为这是相关的
从StackTrace中可以看出,正在创建一个非常大的字符串并导致异常。冒着显得愚蠢的风险-您是否考虑过用另一种方式执行此操作
就我个人而言,我会避免在“远离”数据库的地方进行批处理。我不知道您使用的是什么数据库,但通常有一种机制可以有效地将数据集从数据库中拉出来&拉到文件中,即使它在退出时涉及到适度简单的操作。存储过程、特定的导出实用程序。调查您的数据库供应商还提供了哪些功能。看起来您正在使用起始和结束行号调用getProductIterator(),而getProductIterator()需要起始行和行计数。随着“上限”越来越高,您正在读取更大的数据块。我想您的意思是将batchSize作为第二个参数传递给getProductIterator()。不是一个直接的答案,但对于这种数据操作,我会使用。KeithL是正确的-您正在通过一个不断增加的限制。但是,用这种方式把它拆散是没有意义的。滚动光标的全部意义在于一次处理一行,因此无需将其拆分为块。fetch大小以占用更多内存为代价,减少了对数据库的访问。一般模式应为:
Query q = session.createCriteria(... no offset or limit ...);
q.setCacheMode(CacheMode.IGNORE); // prevent query or second level caching
q.setFetchSize(1000); // experiment with this to optimize performance vs. memory
ScrollableResults iterator = query.scroll(ScrollMode.FORWARD_ONLY);
while (iterator.next()) {
Product p = (Product)iterator.get();
...
session.evict(p); // required to keep objects from accumulating in the session
}
也就是说,错误是getHtmlSources,因此问题可能与会话/光标/滚动问题完全无关。如果这些html字符串很大,并且它们一直都在被引用,那么您可能正在耗尽连续内存
顺便说一句,我没有在ScrollableResults上看到getScrollableResults方法。stacktrace,但我认为gc的调优不会有帮助。我试过System.gc();在读取新批处理之前,put内存仍然溢出。“session.execute(p);//在上面设置缓存模式的替代方法”此语句为false,缓存模式与二级缓存和查询缓存有关,而不是会话本身。驱逐或明确声明仍然是强制性的。Gab是正确的。我更新了答案以反映这一点。
Query q = session.createCriteria(... no offset or limit ...);
q.setCacheMode(CacheMode.IGNORE); // prevent query or second level caching
q.setFetchSize(1000); // experiment with this to optimize performance vs. memory
ScrollableResults iterator = query.scroll(ScrollMode.FORWARD_ONLY);
while (iterator.next()) {
Product p = (Product)iterator.get();
...
session.evict(p); // required to keep objects from accumulating in the session
}