[英]Grails, Inserting lots of data using withTransaction results in OutOfMemoryError
我正在使用Grails 1.1 beta2。 我需要將大量數據導入我的Grails應用程序。 如果我反復實例化一個grails域類然后保存它,那么性能會慢得令人無法接受。 例如,從電話簿導入人員:
for (each person in legacy phone book) {
// Construct new Grails domain class from legacy phone book person
Person person = new Person(...)
person.save()
}
事實證明這很痛苦。 Grails郵件列表上的某人建議在事務中批量保存。 所以現在我有:
List batch = new ArrayList()
for (each person in legacy phone book) {
// Construct new Grails domain class from legacy phone book person
Person person = new Person(...)
batch.add(person)
if (batch.size() > 500) {
Person.withTransaction {
for (Person p: batch)
p.save()
batch.clear()
}
}
}
// Save any remaining
for (Person p: batch)
p.save()
這項工作必須更快,至少在最初階段。 每筆交易可保存500條記錄。 隨着時間的推移,交易需要越來越長的時間。 前幾次交易大約需要5秒鍾,然后它就會從那里開始。 在大約100次交易之后,每次交易都需要一分鍾,這再一次是不可接受的。 更糟糕的是,最終Grails最終會耗盡Java堆內存。 我可以增加JVM堆大小,但這只會延遲OutOfMemoryError
異常。
任何想法為什么會這樣? 就像有一些內部資源沒有被釋放。 性能變得更糟,內存被保留,然后最終系統耗盡內存。
根據Grails文檔 , withTransaction
將閉包傳遞給Spring的TransactionStatus
對象。 我在TransactionStatus
找不到任何關閉/結束事務的東西。
編輯:我是從Grails的控制台( grails console
)運行的
編輯:這是內存不足異常:
Exception thrown: Java heap space
java.lang.OutOfMemoryError: Java heap space
at org.hibernate.util.IdentityMap.entryArray(IdentityMap.java:194)
at org.hibernate.util.IdentityMap.concurrentEntries(IdentityMap.java:59)
at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:113)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:655)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:732)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:701)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
這是所有hibernate應用程序的常見問題,它是由hibernate會話的增長引起的。 我猜測grails控制台以類似於'open view in view'模式的方式為你打開一個hibernate會話,我知道它用於正常的Web請求。
解決方案是獲取當前會話並在每批后清除它。 我不確定如何使用控制台獲取spring bean,通常用於控制器或服務,您只需將它們聲明為成員。 然后,您可以使用sessionFactory.getCurrentSession()
獲取當前會話。 為了清除它,只需調用session.clear()
,或者如果你選擇使用session.evict(Object)
為每個Person
對象。
對於控制器/服務:
class FooController {
def sessionFactory
def doStuff = {
List batch = new ArrayList()
for (each person in legacy phone book) {
// Construct new Grails domain class from legacy phone book person
Person person = new Person(...)
batch.add(person)
if (batch.size() > 500) {
Person.withTransaction {
for (Person p: batch)
p.save()
batch.clear()
}
// clear session here.
sessionFactory.getCurrentSession().clear();
}
}
// Save any remaining
for (Person p: batch)
p.save()
}
}
}
希望這可以幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.