2011-10-17 6 views
36

Quando ho ridistribuire la mia applicazione in Tomcat, ottengo il seguente problema:Perdita di memoria quando ridistribuire applicazione in Tomcat

The web application [] created a ThreadLocal with key of type 
[java.lang.ThreadLocal] (value [[email protected]]) 
and a value of type [com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty] 
(value [[email protected]a183d2]) but 
failed to remove it when the web application was stopped. 
This is very likely to create a memory leak. 

Inoltre, sto usando EHCache nella mia applicazione. Anche questo sembra comportare la seguente eccezione.

 SEVERE: The web application [] created a ThreadLocal with key of type [null] 
    (value [[email protected]]) and a value of type [java 
    .util.WeakHashMap... 

L'EHCache sembra creare una mappa hash debole e ottengo il messaggio che questo è molto probabile che per creare una perdita di memoria.

Ho cercato in rete e ho trovato questo, http://jira.pentaho.com/browse/PRD-3616 ma non ho accesso al server in quanto tale.

Per favore fatemi sapere se questi avvertimenti hanno un impatto funzionale o possono essere ignorati? Ho usato l'opzione "Trova perdite di memoria" in Tomcat Manager e dice "Nessuna perdita di memoria trovata"

+1

Le avvertenze significa che la vostra capacità di ridistribuire l'applicazione senza dover riavviare Tomcat si è limitata. Le webapp sono state a lungo afflitte da perdite di memoria di questo tipo. Non hanno alcun impatto a meno che non si ridistribuisca le app. Non lo so, ma sospetto che questi messaggi nell'output di Tomcat, che hanno ripreso a mostrare un anno o due di nuovo, facciano pressione sui costruttori di framework per iniziare a ripulire correttamente dopo il riavvio. –

risposta

36

Quando si ridistribuisce l'applicazione, Tomcat crea un nuovo caricatore di classe. Il vecchio programma di caricamento classi deve essere sottoposto a garbage collection, altrimenti si ottiene una perdita di memoria permgen.

Tomcat non può controllare se la garbage collection funzionerà o meno, ma conosce diversi punti comuni di errore. Se il caricatore di classe webapp imposta ThreadLocal con un'istanza la cui classe è stata caricata dal caricatore di classi webapp stesso, il thread servlet contiene un riferimento a tale istanza. Ciò significa che il programma di caricamento classi non verrà raccolto automaticamente.

Tomcat esegue un numero di tali rilevamenti, vedere here for more information. La pulizia dei thread locali è difficile, è necessario chiamare remove() su ThreadLocal in ciascuno dei thread a cui è stato effettuato l'accesso. In pratica, ciò è importante solo durante lo sviluppo quando si ridistribuisce l'app Web più volte. In produzione, probabilmente non si ridistribuisci, quindi questo può essere ignorato.

Per scoprire veramente quali istanze definiscono i locali dei thread, è necessario utilizzare un profiler. Ad esempio, il walker dell'heap in JProfiler (disclaimer: la mia azienda sviluppa JProfiler) ti aiuterà a trovare questi thread locali. Selezionare la classe valore riportata (com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty o com.sun.xml.bind.v2.ClassFactory) e mostrare i riferimenti cumulativi in ​​entrata. Uno di questi sarà uno java.lang.ThreadLocal$ThreadLocalMap$Entry. Seleziona gli oggetti di riferimento per quel tipo di riferimento in entrata e passa alla vista delle allocazioni. Vedrai dove è stata allocata l'istanza. Con queste informazioni puoi decidere se puoi fare qualcosa al riguardo o no.

enter image description here

+2

Come vedo, sembra che JProfiler non sia una soluzione open source gratuita. Puoi suggerire qualche altra alternativa – Raghav

+3

Prova [VisualVM] (http://visualvm.java.net/). – timomeinen

+0

@Raghav Eclipse Memory Analyzer è anche ottimo per analizzare i dump dell'heap. http://www.eclipse.org/mat/ – Phil

2

io indovina che probabilmente visto questo, ma solo nel caso in EHCache doc raccomanda di mettere la lib in Tomcat e non in WEB-INF/lib: http://ehcache.org/documentation/integrations/tomcat

+0

Link aggiornato: http://ehcache.org/generated/2.9.0/html/ehc-all/#page/Ehcache_Documentation_Set%2Fco-tcat_tomcat_issues_and_practices.html%23wwconnect_header quindi scorrere fino a "Perdita di memoria del caricatore di classi" –

3

Creazione di discussioni senza ripulire correttamente sarà alla fine ti spazza via dalla memoria - è stato lì, fatto.

Coloro che sono ancora chiedendo per una rapida soluzione/soluzione, può andare qui di seguito:

  • Se l'esecuzione del Tomcat standalone, uccidere javaw.exe o il processo che porta essa.
  • Se in esecuzione da eclissi, kill eclipse.exe e java.exe o processo di inclusione.
  • Ancora non risolto, controllare il task manager, è probabile che il processo che sta causando questo verrà mostrato con l'utilizzo della memoria più alta - Esegui la tua analisi e uccidi quello.

Si dovrebbe essere buona per ridistribuire il materiale e procedere senza problemi di memoria.

6

Mattias Jiderhamn ha un eccellente 6-part article che spiega molto chiaramente la teoria e la pratica sulle perdite di classloader. Ancora meglio, ha anche rilasciato un file jar che possiamo includere nei nostri file di guerra. L'ho provato sulle mie app Web e il file jar ha funzionato come un incantesimo! Il file jar è chiamato classloader-leak-prevention.jar. Per utilizzarlo è semplice come solo l'aggiunta di questo alla nostra web.xml

<listener> 
    <listener-class>se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor</listener-class> 
</listener> 

e poi l'aggiunta di questo alla nostra pom.xml

<dependency> 
    <groupId>se.jiderhamn</groupId> 
    <artifactId>classloader-leak-prevention</artifactId> 
    <version>1.15.2</version> 
</dependency> 

Per ulteriori informazioni, si prega di fare riferimento al project home page hosted on GitHub o Part 6 of his article

1

Si consiglia di inizializzare thread locals, in un ServletRequestListener.

ServletRequestListener dispone di 2 metodi: uno per l'inizializzazione e uno per la distruzione.

In questo modo, è possibile pulire il ThreadLocal. Esempio:

public class ContextInitiator implements ServletRequestListener { 
    @Override 
    public void requestInitialized(ServletRequestEvent sre) { 
     context = new ThreadLocal<ContextThreadLocal>() { 
      @Override 
      protected ContextThreadLocal initialValue() { 
       ContextThreadLocal context = new ContextThreadLocal(); 
       return context; 
      } 
     }; 
     context.get().setRequest(sre.getServletRequest()); 
    } 
    @Override 
    public void requestDestroyed(ServletRequestEvent sre) { 
     context.remove(); 
    } 
} 

web.xml:

<listener> 
    <listener-class>ContextInitiator</listener-class> 
</listener>