2009-11-30 19 views
7

Sto eseguendo un'applicazione Web Java in Tomcat. L'applicazione utilizza il framework Quartz per pianificare il cron job a intervalli regolari. Questo lavoro cron prevede l'analisi di un file xml 4+, che sto facendo usando l'API JDOM. Il file xml contiene circa 3600 nodi da analizzare e di conseguenza i dati da aggiornare in DB che sto facendo in sequenza.
Dopo aver analizzato quasi la metà del file, la mia applicazione genera un'eccezione di memoria insufficiente. La traccia dello stack dello stesso è:Java memoria esaurita Eccezione

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) 

Ciò causa anche il mio tomcat in crash. Potete per favore aiutarmi a diagnosticare il problema. Ho persino abilitato la creazione di profili in Netbeans per lo stesso, ma sembra che anche quello si sia bloccato. Ho mantenuto la memoria predefinita assegnata a Tomcat. C'è qualche perdita di memoria in atto. Il mio DB è postgres e JDK è 1.6.0_15.

Grazie, Amit

+4

non dimenticare di selezionare una risposta per questa e per le domande precedenti; hai già fatto 7 domande e nessuna di loro ha avuto una buona risposta? –

risposta

5

Ogni volta che si utilizza un DOM per analizzare un file XML, potrai caricare intero file in infrastrutture DOM memoria e utilizzerà circa stessa dimensione di gestire, in modo che sarà consumano circa il doppio memoria della dimensione del tuo file.

Avrete bisogno di usare SAX, un parser basato su eventi. Anche se questo può essere difficile da capire per la prima volta, è una memoria molto efficace, in quanto mantiene solo in memoria il nodo di analisi corrente.

Sembra che Java abbia alcune implementazioni SAX, come StAX, spero che sia d'aiuto.

+0

Ciao Rubens, sto usando JDOM per analizzare il grande XML e usa internamente il parser SAX. Il mio codice di parsing è: SAXBuilder builder = new SAXBuilder(); Documento doc = builder.build (inputResource); Elemento elem = doc.getRootElement(); – Amit

+1

poiché il tuo parser DOM utilizza SAX, dovresti leggere il tuo XML in sequenza, e per evitare di usare '..',' // 'e roba –

+0

StAX e SAX sono diverse API. Tuttavia, entrambi possono essere utilizzati per ridurre l'utilizzo della memoria. (SAX usa i callback, nel codice utente StAX chiede il prossimo token analizzato) –

0

Sei sicuro che non ci sia alcuna copia di matrice ricorsiva da qualche parte, lasciata lì per errore? Forse in diversi thread?

+0

Ciao Lorenzog, non sto usando alcun thread, per questo scopo e nessuna copia dell'array. Sto usando JDOM per analizzare il file XML e suppongo che usi ArrayList per la sua implementazione. Può essere un problema? C'è qualche possibilità di perdita di memoria? – Amit

0

In secondo luogo quel punto sul file e il DOM occupa una gran quantità di memoria. Mi chiedo anche quando vedo questo:

ERROR [JobRunShell]: Job updateVendorData.quoteUpdate threw an unhandled Exception: 
    java.lang.OutOfMemoryError: Java heap space 
    at java.util.Arrays.copyOfRange(Arrays.java:3210) 

Cosa sta facendo che la copia? Mi chiedo se ci sia qualcos'altro nel tuo codice.

Se sei arrivato così lontano, suggerisce di aver letto correttamente il file e il DOM e che stai iniziando a scrivere sul database. La memoria del file dovrebbe già essere recuperata.

Suggerirei di guardare la memoria usando VisualGC in modo da poter vedere cosa sta succedendo.

+0

Quella copia è l'interno di StringBuffer. – BalusC

+0

Ciao duffy, grazie per la tua risposta. JDOM può implementare internamente questa copia? Ho sempre abilitato la creazione di profili per l'applicazione e ci sono 2 classi che esistono anche dopo l'esecuzione di GC, sono org.postgresql.jdbc4.Jdbc4PrepareStatement e org.postgresql.jdbc4.Jdbc4ResultSet. Possono essere causa di perdita di memoria nell'applicazione? – Amit

+0

Potrebbe essere. Non posso dire senza codice, ma se non li stai chiudendo correttamente potresti perdere risorse che ti faranno soffrire. – duffymo

2

L'analisi XML è un'attività piuttosto costosa. Il parser DOM medio avrebbe già bisogno di almeno cinque volte dello spazio di memoria come il grande documento XML. Dovresti tener conto anche di questo fatto. Per garantire che non ci siano perdite di memoria da qualche altra parte che ha causato la mancanza di memoria per il parser XML, è davvero necessario eseguire un profiler. Dare più memoria, raddoppiare la memoria disponibile e tracciarlo. Quando hai inchiodato la causa e risolto la perdita, puoi semplicemente tornare alla memoria "predefinita" e ripetere il test. O se non c'è davvero alcun modo di perdita, quindi basta dare un po 'più di memoria di default in modo che tutto si adatta.

Si può anche pensare di utilizzare una più efficiente della memoria parser XML, invece, ad esempio VTD-XML (homepage here, benchmarks here).

1

Hai provato a impostare la dimensione massima dell'heap più grande per vedere se il problema si verifica ancora? Potrebbe non esserci nemmeno una perdita. Potrebbe essere solo che la dimensione dell'heap predefinita (64 milioni su Windows credo) non sia sufficiente per questo particolare processo.

Trovo che ho quasi sempre bisogno di dare qualsiasi applicazione Sto eseguendo Tomcat più heap e perm gen space rispetto ai valori predefiniti o mi imbatterò in problemi di memoria. Se hai bisogno di aiuto per regolare le impostazioni della memoria dai un'occhiata a this question.

+0

Ciao Jason, grazie per la tua risposta. Ho aumentato la dimensione del mio heap e l'applicazione ha funzionato bene. Ho impostato il profilo per la mia applicazione e dopo che il processo è stato completato, è stato possibile trovare 2 tipi di oggetti allocati dal vivo che non sono stati deallocati dal garbage collector, sono org.postgresql.jdbc4.Jdbc4PrepareStatement e org.postgresql.jdbc4.Jdbc4ResultSet. Possono essere causa di perdita di memoria nell'applicazione? – Amit

+0

Potrebbe essere un'indicazione che non stai chiudendo sei oggetti JDBC correttamente. Stai facendo le chiamate JDBC o stai usando un framework (come Spring) per avvolgere le tue chiamate JDBC? Se si sta chiamando direttamente JDBC, assicurarsi di chiamare il metodo close() su qualsiasi oggetto ResultSet, Statement, PreparedStatement e Connection in un blocco finally quando si è finito di usarli. –

+0

Con il meglio delle mie capacità, ho avuto cura di chiudere tutte le connessioni che ho aperto ma il problema esiste ancora. C'è un modo in cui puoi aiutarmi con questo – Amit

0

È possibile eseguire l'applicazione con: -XX: + HeapDumpOnOutOfMemoryError. Ciò farà sì che JVM produca un dump dell'heap quando esaurisce la memoria. Puoi usare qualcosa come: MAT o JHAT per vedere a cosa sono tenuti gli oggetti. Suggerisco di utilizzare lo strumento di analisi della memoria di eclissi (MAT) sul dump dell'heap generato in quanto è abbastanza semplice da utilizzare: http://www.eclipse.org/mat/

Naturalmente è necessario avere qualche idea su quali oggetti possono essere in giro in ordine per questo per essere utile. Oggetti DOM? Risorse da precedenti carichi di documenti xml? Connessioni al database? MAT ti consentirà di rintracciare i riferimenti su un oggetto radice da qualche oggetto che si sospetta dovrebbe essere stato raccolto.

9

Provare ad aumentare l'allocazione ram per JVM. Dovrebbe aiutare

Fix per Eclipse: È possibile configurare questo a preferenza eclissi come segue

  1. Windows -> Preferenze (su Mac la sua: Eclipse -> Preferenze)
  2. Java -> JRE installata
  3. Selezionare il JRE e fare clic su Modifica
  4. sul tipo di campo degli argomenti VM predefinito in -Xmx1024M. (o la tua preferenza di memoria, per 1 GB di RAM è il 1024)
  5. Fare clic su Fine o OK.
+1

Grazie funziona –

2

È necessario allocare più spazio su PermGenSpace della tomcat JVM.

Questo può essere fatto con l'argomento JVM: -XX:MaxPermSize=128m

Per impostazione predefinita, lo spazio PermGen è 64M (e contiene tutte le classi compilate, quindi se avete un sacco di vaso (classi) nel classpath, si può davvero riempire questo spazio).

Su un lato nota, è possibile monitorare la dimensione dello spazio PermGen con JVisualVM e si può anche controllare il suo contenuto con YourKit Java Profiler

3

Provare ad aumentare la dotazione di RAM per la JVM. Dovrebbe aiutare

Fix per Eclipse: È possibile configurare questo a preferenza eclissi come segue

Windows -> Preferenze (su Mac la sua: Eclipse -> Preferenze) Java -> JRE installata Selezionare il JRE e fare clic su Modifica sul default Il campo degli argomenti VM digita --Xms256m -Xmx512m -XX: MaxPermSize = 512m -XX: PermSize = 128m. (o la tua preferenza di memoria, per 1 GB di RAM è il 1024) Fai clic su Fine o OK.

Problemi correlati