2014-10-03 10 views
5

Ho un'app Web distribuita su Tomcat 7.0.54 che utilizza un'origine dati per connettersi a un database Oracle 11g. L'origine dati è configurata in META-INF/context.xml e ho inserito ojdbc7.jar in <tomcat-install-dir>/lib. Io uso una ricerca JNDI per recuperare l'origine dati che immagazzino in un singleton in modo che ogni classe DAO possa usarla.Perdita di memoria su Tomcat 7 con i driver Oracle JDBC 12c - impossibile interrompere il thread oracle.jdbc.driver

Tutto funziona come previsto, tuttavia quando ho annullare la distribuzione dell'applicazione (tramite Tomcat Manager app) che vedo nei log:

Oct 03, 2014 3:06:55 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads 
SEVERE: The web application [/myapp] appears to have started a thread named [oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser] but has failed to stop it. This is very likely to create a memory leak. 
Oct 03, 2014 3:06:57 PM org.apache.catalina.startup.HostConfig undeploy 
INFO: Undeploying context [/myapp] 

Quando il debug posso vedere questa discussione viene creato non appena il database è accesso (tramite l'origine dati).

La mia origine dati di configurazione:

<Context antiResourceLocking="false"> 
    <Resource name="jdbc/myapp" auth="Container" 
     type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver" 
     maxActive="20" maxIdle="10" maxWait="-1" 
     username="myuser" password="mypass" 
     url="jdbc:oracle:thin:@myserver:1521:mysid" 
     removeAbandoned="true" removeAbandonedTimeout="10" logAbandoned="true" 
     validationQuery="SELECT 1 FROM DUAL" 
     testOnBorrow="true" testOnReturn="true" testWhileIdle="true" 
     timeBetweenEvictionRunsMillis="1800000" numTestsPerEvictionRun="3" 
     minEvictableIdleTimeMillis="1800000" 
    /> 
</Context> 

EDIT

Ulteriori indagini hanno rivelato che il problema si verifica se l'origine dati si accede durante l'applicazione (o servlet) inizializzazione.

In realtà, il thread problematico viene creato e quindi il problema esiste solo quando si utilizzano le versioni 12c dei driver JDBC di Oracle (ojdbc6.jar o ojdbc7.jar).

Se si ripristina l'utilizzo della versione 11.2.0.4 di ojdbc6.jar, il thread non viene mai creato e l'avviso di perdita di memoria non viene mai visualizzato.

Devo eseguire il downgrade del driver JDBC (come suggerito in https://stackoverflow.com/a/9177263/4105953)?

+0

Assicurarsi che il driver di Oracle non sia anche nella directory 'WEB-INF/lib'. In questo modo si registrerebbe automaticamente e avvierà alcune delle cose di Oracle JDBC. Vedi anche [qui] (http://stackoverflow.com/questions/3320400/to-prevent-a-memory-leak-the-jdbc-driver-has-been-forcibly-unregistered). –

+0

@ M.Deinum grazie, ma ho già assicurato che il driver JDBC è solo in 'tomcat/lib' e non l'applicazione' WEB-INF/lib' – finchie

risposta

0

Ciò è normale, si verifica quando si distribuisce a caldo in Tomcat. Non ti causerà alcun problema normalmente in produzione perché generalmente non tieni gli aggiornamenti di distribuzione caldi in produzione, basta fermarti e riavviare il server.

+2

Altre applicazioni sono distribuite alla nostra produzione Tomcat, alcune con molto più grande userbases di questa app! Sicuramente non dovrei riavviare la produzione Tomcat solo per ridistribuire un'app? Non è questo quello per cui è l'app Tomcat Manager? – finchie

1

Ho trovato una lunga discussione sull'argomento here. La conclusione è che crea una "perdita di memoria di dimensioni fisse", cioè successivi redeploys non aumenteranno la perdita di memoria.
Non ho accesso al supporto Oracle, ma l'ID bug menzionato nella discussione è 16841748 (maggio 2013, potrebbe essere risolto ora).

Una possibile soluzione è quella di utilizzare effettivamente l'origine dati una volta (ottenere connessione, eseguire una query fittizia, chiudere la connessione) quando Tomcat viene avviato tramite un servlet personalizzato configurato per "caricamento all'avvio" in tomcat/conf/web.xml. Questo dovrebbe avviare il/i thread/i del driver Oracle (vedere anche lo FAQ sui thread del driver) al di fuori dell'ambito del programma di caricamento classi della tua web-app, evitando così la "perdita di memoria di dimensione fissa".

Si noti che esiste un problema simile per il driver JDBC MySQL ma ha un decent solution. Tale soluzione potrebbe esistere per una versione recente del driver JDBC Oracle (non lo so).

+0

Grazie per il suggerimento di soluzione "load-on-startup" - sfortunatamente questo non ha alcun effetto e l'avviso di perdita di memoria persiste durante la distribuzione. – finchie

+0

Hmm, ha cercato nel codice decompilato e ha scoperto che ogni oracle.jdbc.driver.PhysicalConnection crea un thread "BlockSource". Questo spiega perché "load-on-startup" non funziona (ho pensato che fosse un thread generale avviato dal driver, non per connessione). Utilizzando la versione precedente del driver, come descritto nella domanda aggiornata, sembra una soluzione migliore. – vanOekel

+1

Penso che la discussione riguardi in realtà un problema simile ma non correlato, motivo per cui la soluzione alternativa non ha avuto alcun effetto. Tuttavia, esiste un ID documento Oracle per questo e una patch disponibile: [JDBC Thin Driver perde i thread con Tomcat (ID documento 1901449.1)] (https://support.oracle.com/epmos/faces/DocContentDisplay?id=1901449.1) – Mike

-1

Questo problema non si verifica solo con il driver JDBC Oracle (oracle.jdbc.driver.BlockSource.ThreadedCachingBlockSource.BlockReleaser).

Se una delle dipendenze di terze parti avvia un thread daemon per eseguire alcune attività, quindi durante l'arresto dei registri di Tomcat, viene visualizzato anche un messaggio di avviso.

Inoltre, questo problema esiste con le versioni di Tomcat 8.5.X.

Soluzione: Quello che ho trovato come soluzione è ottenere il Thread Group per il thread corrente e interromperlo. Si assicurerà che prima di spegnere Tomcat tutti i suoi thread daemon vengano uccisi.

codice seguente deve essere aggiunto al metodo "contextDestroyed" ThreadGroup threadGroup = Thread.currentThread(). GetThreadGroup(); threadGroup.interrupt();

Problemi correlati