Je gère une application Web Java à Tomcat. L'application utilise un cadre de quartz pour planifier le travail cron à intervalles réguliers. Ce travail de cron implique l'analyse d'un fichier XML de 4+ MB que je fais avec l'API JDOM. Le fichier XML contient environ 3600 nœuds à analyser et par conséquent des données à mettre à jour dans DB que je le fais de manière séquentielle.
Après avoir analysé près de la moitié du fichier, mon application jette une exception hors mémoire. La trace de la pile de la même manière est la suivante:
Exception in thread "ContainerBackgroundProcessor[StandardEngine[Catalina]]" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOfRange(Arrays.java:3210) at java.lang.String.<init>(String.java:216) at java.lang.StringBuffer.toString(StringBuffer.java:585) at org.netbeans.lib.profiler.server.ProfilerRuntimeMemory.traceVMObjectAlloc(ProfilerRuntimeMemory.java:170) at java.lang.Throwable.getStackTraceElement(Native Method) at java.lang.Throwable.getOurStackTrace(Throwable.java:590) at java.lang.Throwable.getStackTrace(Throwable.java:582) at org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:155) at org.apache.juli.logging.DirectJDKLog.error(DirectJDKLog.java:135) at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1603) at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1610) at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1590) at java.lang.Thread.run(Thread.java:619) Exception in thread "*** JFluid Monitor thread ***" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:2760) at java.util.Arrays.copyOf(Arrays.java:2734) at java.util.Vector.ensureCapacityHelper(Vector.java:226) at java.util.Vector.add(Vector.java:728) at org.netbeans.lib.profiler.server.Monitors$SurvGenAndThreadsMonitor.updateSurvGenData(Monitors.java:230) at org.netbeans.lib.profiler.server.Monitors$SurvGenAndThreadsMonitor.run(Monitors.java:169) Nov 30, 2009 2:22:05 PM org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor processChildren SEVERE: Exception invoking periodic operation: java.lang.OutOfMemoryError: Java heap space at java.lang.StringCoding$StringEncoder.encode(StringCoding.java:232) at java.lang.StringCoding.encode(StringCoding.java:272) at java.lang.String.getBytes(String.java:946) at java.io.UnixFileSystem.getLastModifiedTime(Native Method) at java.io.File.lastModified(File.java:826) at org.apache.catalina.startup.HostConfig.checkResources(HostConfig.java:1175) at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1269) at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:296) at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:118) at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1337) at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1601) at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1610) at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1590) at java.lang.Thread.run(Thread.java:619) ERROR [JobRunShell]: Job updateVendorData.quoteUpdate threw an unhandled Exception: java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOfRange(Arrays.java:3210) at java.lang.String.<init>(String.java:216) at java.lang.StringBuffer.toString(StringBuffer.java:585) at org.apache.commons.dbcp.PoolingConnection$PStmtKey.hashCode(PoolingConnection.java:296) at java.util.HashMap.get(HashMap.java:300) at org.apache.commons.pool.impl.GenericKeyedObjectPool.decrementActiveCount(GenericKeyedObjectPool.java:1085) at org.apache.commons.pool.impl.GenericKeyedObjectPool.returnObject(GenericKeyedObjectPool.java:882) at org.apache.commons.dbcp.PoolablePreparedStatement.close(PoolablePreparedStatement.java:80) at org.apache.commons.dbcp.DelegatingStatement.close(DelegatingStatement.java:168) at com.netcore.smsapps.stock.db.CompanyDaoImpl.updateCompanyQuote(CompanyDaoImpl.java:173) at com.netcore.smsapps.stock.vendor.MyirisVendor.readScripQuotes(MyirisVendor.java:159) at com.netcore.smsapps.stock.update.StockUpdateData.execute(StockUpdateData.java:38) at org.quartz.core.JobRunShell.run(JobRunShell.java:207) at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:525) DEBUG [ExceptionHelper]: Detected JDK support for nested exceptions. ERROR [ErrorLogger]: Job (updateVendorData.quoteUpdate threw an exception. org.quartz.SchedulerException: Job threw an unhandled exception. [See nested exception: java.lang.OutOfMemoryError: Java heap space] at org.quartz.core.JobRunShell.run(JobRunShell.java:216) at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:525) Caused by: java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOfRange(Arrays.java:3210) at java.lang.String.<init>(String.java:216) at java.lang.StringBuffer.toString(StringBuffer.java:585) at org.apache.commons.dbcp.PoolingConnection$PStmtKey.hashCode(PoolingConnection.java:296) at java.util.HashMap.get(HashMap.java:300) at org.apache.commons.pool.impl.GenericKeyedObjectPool.decrementActiveCount(GenericKeyedObjectPool.java:1085) at org.apache.commons.pool.impl.GenericKeyedObjectPool.returnObject(GenericKeyedObjectPool.java:882) at org.apache.commons.dbcp.PoolablePreparedStatement.close(PoolablePreparedStatement.java:80) at org.apache.commons.dbcp.DelegatingStatement.close(DelegatingStatement.java:168) at com.netcore.smsapps.stock.db.CompanyDaoImpl.updateCompanyQuote(CompanyDaoImpl.java:173) at com.netcore.smsapps.stock.vendor.MyirisVendor.readScripQuotes(MyirisVendor.java:159) at com.netcore.smsapps.stock.update.StockUpdateData.execute(StockUpdateData.java:38) at org.quartz.core.JobRunShell.run(JobRunShell.java:207)
9 Réponses :
Chaque fois que vous utilisez un DOM pour analyser un fichier XML, vous chargerez un fichier complet dans l'infrastructure de mémoire et DOM utilisera à peu près la même taille pour le gérer. Il suffira donc d'environ deux fois mémoire que votre taille de fichier. P >
Vous devez utiliser SAX, un analyseur à base d'événements. Bien que cela puisse être difficile à comprendre cela pour la première fois, c'est une mémoire très efficace, car il ne conserve que dans le nœud d'analyse de la mémoire. P>
semble que Java ait des implémentations saxoques, telles que Stax , j'espère que cela aide. p>
Bonjour Rubens, j'utilise JDom pour analyser le grand XML et il utilise la SAX Parser en interne. Mon code analysant est: Saxbuilder Builder = Nouveau Saxbuilder (); Document doc = builder.build (INPUTRESOURCE); Élément elem = doc.getrootelement ();
Étant donné que votre Dom Parser utilise sax, vous devez lire votre XML séquentiellement et éviter d'utiliser .. code>,
// code> etc.
Stax et Sax sont des API différentes. Cependant, ils peuvent tous les deux être utilisés pour réduire l'utilisation de la mémoire. (SAX utilise des rappels, dans le code d'utilisateur STAX, demande le prochain jeton analysé)
Êtes-vous sûr qu'il n'y a pas de copie de matrice récursive quelque part, laissée par erreur? Peut-être dans des fils différents? P>
Bonjour Lorenzog, je n'utilise aucun fil, à cette fin et aucune copie de tableau. J'utilise JDOM pour analyser le fichier XML et je suppose qu'il utilise ArrayList pour sa mise en œuvre. Cela peut-il être un problème? Y a-t-il une possibilité de fuite de mémoire?
Je vais second que le point sur le fichier et le DOM prennent beaucoup de mémoire. Je me demande aussi quand je vois ceci: Qu'est-ce que la copie de faire? Je me demande s'il y a quelque chose d'autre mauvais dans votre code. P> Si vous avez beaucoup compris, cela suggère que vous avez lu le fichier et le DOM avec succès et que vous commencez à écrire à la base de données. La mémoire de fichier doit déjà être récupérée. P> Je suggère de regarder la mémoire à l'aide de VISUALGC Pour que vous puissiez voir ce qui se passe. p> p>
Que la copie est les internes de Stringbuffer.
Bonjour Duffy, merci pour votre réponse. JDOM peut-il implémenter ici cette copie? J'ai toujours activé le profilage de l'application et il y a 2 classes qui existent même après que GC a fonctionné, ils sont org.postgresql.jdbc4.jdbc4preparation et org.postgresql.jdbc4.jdbc4Resultst. Celles-ci peuvent-elles être une fuite de mémoire dans l'application?
Pourrait être. Je ne peux pas dire sans code, mais si vous ne les fermez pas correctement, vous pourriez faire fuir des ressources qui vous apporteront du chagrin.
Avec le meilleur de mes capacités, j'ai pris soin de fermer toutes les connexions que j'ai ouvertes, mais le problème existe toujours. Y a-t-il quelque chose que vous pouvez m'aider avec cela
Analyse XML est une tâche assez coûteuse. L'analyseur DOM moyen aurait déjà besoin d'au moins Vous pouvez également envisager d'utiliser un analyseur XML plus efficace de mémoire Mémoire, par exemple VTD-XML ( page d'accueil ici , Benchmarks ici ). P>
Avez-vous essayé de définir la taille maximale du tas plus gros pour voir si le problème survient toujours alors? Il peut y avoir même une fuite du tout. Il se peut que la taille du tas par défaut (64 m sous Windows, je pense) est insuffisante pour ce processus particulier. P>
Je trouve que j'ai presque toujours besoin de donner une application que je passe à Tomcat plus de tas et de Perm Gen espace que les valeurs par défaut ou je vais passer à des problèmes de mémoire. Si vous avez besoin d'aide pour ajuster les paramètres de mémoire, jetez un coup d'œil à Cette question . p>
Bonjour Jason, merci pour votre réponse. J'ai augmenté ma taille de tas et l'application fonctionnait tout simplement bien. J'ai défini le profilage de ma candidature et, après la fin du processus, vous pourriez trouver 2 types d'objets alloués en direct n'ayant pas été traités par le collectionneur de déchets, ils sont org.postgresql.jdbc4.jdbc4preparation et org.postgresql.jdbc4.jdbc4Resultst. Celles-ci peuvent-elles être une fuite de mémoire dans l'application?
Cela pourrait être une indication que vous ne fermez pas vos objets JDBC correctement. Faites-vous les appels JDBC vous-même ou utilisez-vous un cadre (comme Spring) pour envelopper vos appels JDBC? Si vous appelez directement JDBC directement, assurez-vous d'appeler la méthode Fermer () sur n'importe quel résultatlev, instruction, préparé et d'objets de connexion dans un bloc enfin lorsque vous avez fini de les utiliser.
Avec le meilleur de mes capacités, j'ai pris soin de fermer toutes les connexions que j'ai ouvertes, mais le problème existe toujours. Y a-t-il quelque chose que vous pouvez m'aider avec cela
Vous pouvez exécuter votre application avec: -xx: + tasdumponOutofMemororyError. Cela provoquera que la JVM produira un dépotoir de tas lorsque celle-ci manque de mémoire. Vous pouvez utiliser quelque chose comme: mat ou Jhat pour voir quels objets sont tenus à. Je suggère d'utiliser l'outil Eclipse Memory Analyzer (Tapis) sur la décharge de tas générés, car il est assez simple d'utiliser: HTTP : //www.eclipse.org/mat/ p>
Bien sûr, vous devrez avoir une idée de ce que les objets peuvent être suspendus pour que cela soit utile. Objets Dom? Ressources de charges antérieures de documents XML? Connexions de base de données? Mat vous permettra de retracer les références à un objet racine à partir d'un objet que vous soupçonnez avoir été recueilli. P>
Essayez d'augmenter l'allocation de RAM pour votre JVM. Cela devrait aider. P>
Correction pour Eclipse: vous pouvez le configurer dans la préférence Eclipse comme suit P>
Vous devez allouer plus d'espace au PermgenSpace de Tomcat JVM. P>
Ceci peut être fait avec l'argument JVM: Par défaut, l'espace Permgen est de 64 m (et contient toutes les classes compilées, donc si vous avez beaucoup de bocal (classes) dans votre classe de classe, vous pouvez en effet remplir cet espace). P>
sur une note latérale, vous pouvez surveiller la taille de l'espace Permgen avec jvisuelvm et vous pouvez même inspecter Son contenu avec Yourkit Java Profiler P> -xx: maxpermsize = 128m code> p>
Essayez d'augmenter l'allocation de RAM pour votre JVM. Cela devrait aider. P>
Vous pouvez configurer cela dans les préférences d'Eclipse comme suit: P>
Windows -> Préférences (sur Mac It's: Eclipse -> Préférences) Java -> JRES installées EM> P>
Sélectionnez le jre et cliquez sur Modifier sur le type de champ d'arguments VM par défaut dans -XMS256M -XMX512M -XX: MAXPERMSIZE = 512M -XX: Permsize = 128m Code> (ou votre préférence de mémoire, pour 1 Go de RAM c'est 1024). Cliquez sur Terminer ou OK. P>
N'oubliez pas de choisir une réponse pour cette question et des questions précédentes; Vous avez déjà fait 7 questions et aucun d'entre eux n'avait une bonne réponse?