J'utilise Grails 1.1 beta2. J'ai besoin d'importer une grande quantité de données dans mon application Grails. Si j'instancie plusieurs fois une classe de domaine grails et que je l'enregistre ensuite, la performance est trop lente. Prenez par exemple l'importation de personnes à partir d'un annuaire téléphonique:Grails, L'insertion de beaucoup de données en utilisant withTransaction aboutit à OutOfMemoryError
for (each person in legacy phone book) {
// Construct new Grails domain class from legacy phone book person
Person person = new Person(...)
person.save()
}
Cela s'avère être extrêmement lent. Quelqu'un sur la liste de diffusion Grails suggère de sauvegarder les sauvegardes dans une transaction. Donc maintenant j'ai:
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()
Ce travail doit être plus rapide, au moins initialement. Chaque transaction enregistre 500 enregistrements. Au fil du temps, les transactions prennent plus de temps et plus. Les premières transactions prennent environ 5 secondes, puis elles viennent juste de là. Après environ 100 transactions, chacune prend plus d'une minute, ce qui est encore une fois inacceptable. Le pire est que finalement Grails finira par manquer de mémoire de tas Java. Je peux augmenter la taille du tas JVM, mais cela retarde juste l'exception OutOfMemoryError
.
Des idées pourquoi c'est? C'est comme si une ressource interne n'était pas publiée. Les performances s'aggravent, la mémoire est maintenue, puis le système manque de mémoire.
Selon le Grails documentation, withTransaction
passe la fermeture à l'objet TransactionStatus
de Spring. Je n'ai rien trouvé dans TransactionStatus
pour fermer/terminer la transaction.
Edit: Je suis en cela de la console (grails console
)
de Grails Edit: Voici le hors d'exception de la mémoire:
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)
Quel est le contexte dans lequel cela s'exécute? Un travail à quartz? Un contrôleur? Quand nous l'avons fait dans le passé en utilisant un contrôleur nous a permis de définir une boucle qui peut lier la taille du lot, avec la taille de la transaction suivante dans le Service en conséquence –