2010-11-15 20 views
0

Je cours un long lot sur mon application Grails. Le service contacte un service Web et télécharge le fichier XML qui est stocké localement dans l'application grails (dans la base de données). Les objets téléchargés sont assez volumineux et complexes et l'application les traite et crée des objets de domaine local. J'utilise Grails 1.2.2 parce que je ne pouvais pas mettre à jour mon application à la version la plus récente (j'ai passé quelques heures dessus et j'ai jeté l'éponge).OutOfMemoryError pendant un long batch sur Grails/Tomcat

C'est fondamentalement une longue boucle avec peu d'informations partagées entre les itérations.

Il est quelque chose comme:

while(stillObjectsToDo){ 
    def bigObj = myservice.fetchXML 
    def localInstance = myservice.processObj(bigObj) 
    localInstance.saveEverythingToDB 
    clearGORM 
} 

je lance l'application sur Tomcat, qui a été peaufiné pour augmenter la taille du tas. Lorsque je traite un seul objet, je n'ai jamais eu de problème. Mais quand je lance le lot complet (environ 1500 grands objets), je reçois toujours:

codehaus.groovy.grails.web.servlet.mvc.exceptions.ControllerExecutionException: Executing action [runSampleBatch] of controller [semanticopenstreetmap.EngineController] caused exception: java.lang.OutOfMemoryError: Java heap space 
at java.lang.Thread.run(Thread.java:619) 
Caused by: org.codehaus.groovy.runtime.InvokerInvocationException: java.lang.OutOfMemoryError: Java heap space 
... 1 more 
Caused by: java.lang.OutOfMemoryError: Java heap space 
at java.util.Arrays.copyOf(Arrays.java:2882) 

Il semble une sorte de fuite de mémoire, mais je ne peux pas le localiser. C'est vraiment étrange parce que les objets sont stockés dans la base de données et ne sont plus utilisés dans la procédure, donc ils devraient juste être rincés.

J'ai essayé les solutions suivantes pour éviter le problème avec pas de chance:

  • Effacer Session Hibernate après le rinçage chaque objet à chaque itération
  • Appel garbage collector toutes les 5-6 minutes.
  • annule manuellement l'objet précédent à la fin du cycle (probablement inutile).

Rien de tout cela n'a fonctionné efficacement. L'utilisation de la mémoire ne cesse de croître. je Virtual VM et je suis arrivé ce graphique: http://img12.imageshack.us/img12/5660/memoryleak.png Et les classes: http://img263.imageshack.us/img263/331/memoryleakclasses.png

Comment puis-je localiser et résoudre cette fuite de mémoire?

EDIT: Cela aurait-il un sens d'exécuter chaque objet dans un fil séparé ? La fuite de mémoire se produit dans un processus http-0-x, comment puis-je inspecter cet objet?

Des indices?

Merci!

Mulone

+0

quel collecteur grabage utilisez-vous? –

+0

J'utilise celui par défaut: Runtime.getRuntime(). TotalMemory() et Runtime.getRuntime(). FreeMemory() – Mulone

Répondre

1

j'utiliser VM visuel pour attacher au processus et voir ce qui se passe avec la mémoire.

Je me demande si les proxies générés dynamiquement remplissent votre espace perm gén. Visual VM vous donnera les preuves dont vous avez besoin pour voir si cela est vrai.

+0

il ne semble pas être un problème permgen de la trace; il semble être un MOO "normal". – hvgotcodes

+0

J'ai joint le graphique: http://img12.imageshack.us/img12/5660/memoryleak.png – Mulone

1

Vous devez joindre un profileur externe à votre application. En voici un bon.

https://visualvm.dev.java.net/

L'idée de base est que vous ajoutez quelques options à vos scripts de démarrage de tomcat, lancez tomcat et votre profileur, et utilisez votre application. Vous verrez des mises à jour en temps réel et pourrez regarder votre mémoire consommée, libérée, et ainsi de suite. Ce que vous devriez voir est un graphique qui monte, puis tombe, puis augmente, puis tombe, et ainsi de suite. Le point clé est que, si l'utilisation de la mémoire augmente périodiquement, il n'y a pas de tendance à la hausse de la ligne de base au fil du temps. Donc, lorsque le graphique tombe, au fil du temps, il revient à une base de référence. Si, après la chute du graphique, vous constatez une tendance à la hausse, vous devez identifier la zone de l'application qui la provoque. VisualVM a des moyens d'analyser le tas afin que vous puissiez voir combien d'objets de chaque type sont présents. Vous devez utiliser cet outil pour déterminer quels objets ne sont pas collectés.

+0

J'utilise Visual VM, et je peux voir que le tas se développe très très lentement jusqu'à ce qu'il génère une erreur de mémoire insuffisante . J'appelle le GC toutes les 5-6 minutes mais cela ne semble pas résoudre le problème. En fait, si j'appelle le GC de Visual VM (à partir du bouton), cela fonctionne beaucoup mieux. Une idée? – Mulone

+0

@mulone, mis à jour ma réponse – hvgotcodes

1

Il y a une fuite de mémoire connue dans Gorm qui ne peut être fixé en décochant la session. Burt Beckwith l'a écrit here. Fondamentalement, la solution est d'appeler DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP.get().clear()

+0

Merci! J'ai déjà inclus cela dans la fonction clearGORM mais cela ne semble pas fonctionner – Mulone

+0

De l'un de vos screengrabs de profileur, il semble y avoir un grand nombre d'objets char [] qui polluent le tas. Est-ce que votre profileur vous permet de voir où ces instances sont allouées? Peut-être que le service qui récupère le XML cache les réponses quelque part? – BungleFeet