簡體   English   中英

Grails,使用withTransaction插入大量數據會導致OutOfMemoryError

[英]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)

Ted Naleid寫了一篇關於提高批處理性能的博客文章 包括這里作為參考。

這是所有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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM