2012-06-07 27 views
7

Sono in esecuzione Tomcat7, il server è abbastanza potente, 8 GB di RAM 8-core.Gestione memoria Tomcat

tomcat taking a LOT of memory for nothing ?

Il mio problema è che la memoria RES è geting più in alto, fino a quando il server semplicemente non risponde più, neanche chiamando OnOutOfMemoryError.

configurazione di Tomcat:

-Xms1024M 
-Xmx2048M 
-XX:PermSize=256m 
-XX:MaxPermSize=512m 
-XX:+UseConcMarkSweepGC 
-XX:OnOutOfMemoryError='/var/tomcat/conf/restart_tomcat.sh' 

informazioni di memoria:

Memory:  Non heap memory = 106 Mb (Perm Gen, Code Cache), 
Loaded classes = 14,055, 
Garbage collection time = 47,608 ms, 
Process cpu time = 4,296,860 ms, 
Committed virtual memory = 6,910 Mb, 
Free physical memory = 4,906 Mb, 
Total physical memory = 8,192 Mb, 
Free swap space = 26,079 Mb, 
Total swap space = 26,079 Mb 
Perm Gen memory: 88 Mb/512 Mb ++++++++++++ 
Free disk space: 89,341 Mb 

La memoria utilizzata da Tomcat non sembra così in alto rispetto al comando superiore.

app memory graph

Ho anche avuto java.net.SocketException: No buffer space available quando si cerca di connettersi al server SMTP o quando si cerca di connettersi al server di Facebook.

utilizzare Hibernate, con pool di connessioni c3p0 con questa configurazione:

 <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> 
     <property name="hibernate.connection.url">jdbc:mysql://urldb/schema?autoReconnect=true</property> 
     <property name="hibernate.connection.username">username</property> 
     <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property> 
     <property name="hibernate.connection.password"></property> 
     <property name="connection.characterEncoding">UTF-8</property> 

     <property name="hibernate.c3p0.acquire_increment">1</property> 
     <property name="hibernate.c3p0.idle_test_period">300</property> 
     <property name="hibernate.c3p0.timeout">5000</property> 
     <property name="hibernate.c3p0.max_size">50</property> 
     <property name="hibernate.c3p0.min_size">1</property> 
     <property name="hibernate.c3p0.max_statement">0</property> 
     <property name="hibernate.c3p0.preferredTestQuery">select 1;</property> 
     <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> 

non ho trovato nulla ... Qualcuno ha un accenno di dove dovrei cercare?

Grazie!

[UPDATE 1] MUCCHIO DUMP:

HEAP HISTOGRAM : 

class [C         269780 34210054 
class [B         5600 33836661 
class java.util.HashMap$Entry    221872 6212416 
class [Ljava.util.HashMap$Entry;   23797 6032056 
class java.lang.String      271170 5423400 
class org.hibernate.hql.ast.tree.Node  103588 4972224 
class net.bull.javamelody.CounterRequest 28809 2996136 
class org.hibernate.hql.ast.tree.IdentNode 23461 2205334 
class java.lang.Class      14677 2113488 
class org.hibernate.hql.ast.tree.DotNode 13045 1852390 
class [Ljava.lang.String;     48506 1335600 
class [Ljava.lang.Object;     12997 1317016 


Instance Counts for All Classes (excluding platform) : 

103588 instances of class org.hibernate.hql.ast.tree.Node 
33366 instances of class antlr.ANTLRHashString 
28809 instances of class net.bull.javamelody.CounterRequest 
24436 instances of class org.apache.tomcat.util.buf.ByteChunk 
23461 instances of class org.hibernate.hql.ast.tree.IdentNode 
22781 instances of class org.apache.tomcat.util.buf.CharChunk 
22331 instances of class org.apache.tomcat.util.buf.MessageBytes 
13045 instances of class org.hibernate.hql.ast.tree.DotNode 
10024 instances of class net.bull.javamelody.JRobin 
9084 instances of class org.apache.catalina.loader.ResourceEntry 
7931 instances of class org.hibernate.hql.ast.tree.SqlNode 

[UPDATE 2] server.xml:

<Connector port="8080" protocol="HTTP/1.1" 
       connectionTimeout="20000" 
       redirectPort="8443" 
       URIEncoding="UTF-8" 
       maxThreads="150" 
       minSpareThreads="25" 
       maxSpareThreads="75" 
       enableLookups="false" 
       acceptCount="1024" 
       server="unknown" 
       address="public_ip" 
    /> 

**** [UPDATE 3] Uscita dai file di log: ****

2012-06-04 06:18:24,152 [http-bio-ip-8080-exec-3500] ERROR org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/api].[Jersey REST Service]- Servlet.ser 
vice() for servlet [Jersey REST Service] in context with path [/socialapi] threw exception 
java.net.SocketTimeoutException: Read timed out 

    at java.net.SocketInputStream.socketRead0(Native Method) 
    at java.net.SocketInputStream.read(SocketInputStream.java:129) 
    at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:532) 
    at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:501) 
    at org.apache.coyote.http11.InternalInputBuffer$InputStreamInputBuffer.doRead(InternalInputBuffer.java:563) 
    at org.apache.coyote.http11.filters.IdentityInputFilter.doRead(IdentityInputFilter.java:118) 
    at org.apache.coyote.http11.AbstractInputBuffer.doRead(AbstractInputBuffer.java:326) 
    at org.apache.coyote.Request.doRead(Request.java:422) 

[AGGIORNAMENTO 4] ServletContext

Io uso un ServletContextListener nella mia applicazione per instanciate controllori e mantenere un punto di riferimento con event.getServletContext().setAttribute. Questi controller caricano configurazioni e traduzioni (gli 88 Mb in Perm).

quindi di utilizzare il database che uso:

SessionFactory sf = dbManager.getSessionFactory(DatabaseManager.DB_KEY_DEFAULT); 
Session session = sf.openSession(); 
Transaction tx = null; 

try { 
    tx = session.beginTransaction(); 

    //Do stuuf 

    tx.commit(); 

} catch (Exception e){ 
    //Do something 
} finally { 
    session.close(); 
} 
  1. Potrebbe essere questa la fonte di una perdita?
  2. Perché non utilizzare la transazione/sessione manuale e come faresti allora?
+0

Quando il server smette di rispondere, dovresti anche ottenere un dump del thread (vedi http://wiki.apache.org /tomcat/HowTo#How_do_I_obtain_a_thread_dump_of_my_running_webapp_.3F) per vedere cosa sta facendo la tua JVM. Puoi anche postare la tua configurazione di da server.xml, comunicarci eventuali caratteristiche di "connessione abbandonata" abilitate nel tuo DBCP e dirci anche qual è il limite di handle di file per il tuo processo JVM? Qualche cosa nei log - in particolare logs/catalina.out (o dovunque stdout vada)? –

+0

Aggiornamento 2: server.xml. OK grazie, proverò a jstack la prossima volta che bloccherà! sulla mia lista di processi mysql posso vedere una connessione in attesa, niente di strano. Nei registri l'ho messo nell'aggiornamento 3 –

+0

L'applicazione utilizza molti HQL? Annotazioni o xml di ibernazione? L'HQL è statico finale? – ssedano

risposta

14

prova con questo parametro:

+XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpPath=dump.log 

provare anche con la memoria avvio inferiore parametri -Xms.

quindi è possibile controllare il dump per vedere se il problema era l'allocazione dell'oggetto.

durante l'esecuzione di prova

jps 

che sarà in uscita tutti i processi Java, consente di dire Tomcat è PID 4444:

jmap -dump:format=b,file=heapdump 4444 

E

jhat heapdump 

Se si esaurisce la memoria mentre l'esecuzione di jhat aggiunge solo più memoria. Da lì puoi ispezionare l'heap della tua applicazione.

Un altro modo per andare è abilitare le statistiche di Hibernate per verificare che non si stiano recuperando più oggetti. Anche se sembra una raccolta dei rifiuti completa ogni ora non dovrebbe essere un problema (spazio per farlo meglio lì).

-verbose:gc -Xloggc:/opt/tomcat/logs/gc.out -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 

E con GCViewer per esempio dare un'occhiata a ogni spazio di memoria (ternured, eden, i sopravvissuti, perm).

Un altro strumento a portata di mano:

jstack 4444 > stack.txt 

che recupererà una traccia stack completo di ogni thread in esecuzione all'interno del processo di Java con PID 4444.

Tenete a mente che è necessario privilegi se hai iniziato Tomcat come root o un altro utente.

jps 

non genererà un processo che non ha privilegi, quindi non è possibile connettersi ad esso.

Dato che non so di cosa si tratta la tua applicazione (e quindi non conosco i suoi requisiti) 3 milioni di istanze sembrano molto.

Con Hibernate statistics è possibile vedere quali classi si istanziano di più.

Quindi il tunning delle proporzioni della ricognizione eden and ternured può essere più efficiente.

Gli oggetti appena istanziati vanno in eden. Quando si riempie un grilletto gc minore. Ciò che non è cancellato va in uno spazio sopravvissuto. Quando questo si riempie va a rinsaldarsi. Il pieno gc sorgerà quando il ternured è pieno.

In questa immagine (che è inaccurata) ho lasciato da parte String che diventano internati e file mappati in memoria (che non sono in heap). Dai un'occhiata a quali classi hai istanziato di più. L'uso intensivo di String potrebbe portare a riempire rapidamente perm.

Immagino di farlo, ma utilizzare un factory di sessione gestito, ad esempio Spring (se presente nello stack) ed evitare la gestione manuale delle transazioni e delle sessioni.

Ricordare che gli oggetti vengono eliminati nel GC quando nessun oggetto si riferisce ad esso. Quindi finché un oggetto è raggiungibile nella tua applicazione l'oggetto rimane.

Se il server ServletContextListener istanzia i controller e sono memorizzati nell'evento getServletContext. Assicurati di rimuovere completamente il riferimento in seguito, se mantieni un riferimento gli oggetti non verranno eliminati, poiché sono ancora raggiungibili.

Se si gestiscono le proprie transazioni e sessione (che va bene se non è possibile utilizzare un framework), è necessario gestire la manutenzione del codice e bug che, ad esempio, hanno risolto e migliorato Spring-tx.

Personalmente avrei approfittato di FOSS. Ma ovviamente a volte non è possibile ingrandire lo stack.

Se si utilizza Hibernate, darei un'occhiata a Spring-orm e Spring-tx per gestire le transazioni e la sessione. Dai un'occhiata anche allo Hibernate patter Open Session In View.

+0

grazie, lo aggiungo e controllo il dump dell'heap (l'ho controllato da javamelody, sembrava ok e corrispondente al grafico: Heap Classes: 4,367, Istanze: 3,376,160, Kilo-Bytes: 372,020) –

+0

Grazie, come il mio server funziona normalmente ora jstack fornisce informazioni "inutili", ma lo farò se il congelamento avviene di nuovo con uno script –

+0

Ma se l'ibernazione stava perdendo oggetti, non sarebbe rimasto nella "memoria usata" dell'applicazione (come: org.hibernate.hql.ast.tree.Node \t 240.128 Istanze 11 MB)? –

2

Vorrei anche consigliare di scaricare Visual VM 1.3.3, installare tutti i plugin e collegarlo al PID Tomcat in modo da poter vedere cosa succede in tempo reale. Perché aspettare una discarica di thread? Ti dirà anche CPU, thread, tutte le generazioni di heap, quali oggetti consumano più memoria, ecc.

+0

per farlo dovrà aggiungere i parametri di avvio. Anche dump di dump su richiesta. jvisualvm è quasi altrettanto buono e senza download. -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port = 9004 -Dcom.sun.management.jmxremote.ssl = false -Dcom.sun.management.jmxremote.authenticate = false – ssedano

+0

Ho provato YourKit (che sembra simile a Visual VM), purtroppo non mi ha aiutato a risolvere il problema (avrei potuto usarlo solo in ambiente di sviluppo), c'è un modo per collegarlo ai server di produzione? –

+0

Grazie ssedano ma vuol dire che apri una porta JMX come pubblica che è qualcosa che non riesco a immaginare –