Para evitar um memory leaks, o driver JDBC foi forçado a não registrar

Estou recebendo esta mensagem quando executo meu aplicativo da web. Ele funciona bem, mas eu recebo essa mensagem durante o desligamento.

GRAVE: Um aplicativo da web registrou o driver JBDC [oracle.jdbc.driver.OracleDriver], mas não cancelou o registro quando o aplicativo da Web foi interrompido. Para evitar um memory leaks, o driver JDBC foi forçado a cancelar o registro.

Qualquer ajuda apreciada.

   

Desde a versão 6.0.24, o Tomcat é fornecido com um recurso de detecção de memory leaks , que pode levar a esse tipo de aviso quando há um driver compatível com JDBC 4.0 no /WEB-INF/lib que se auto registra durante a boot do webapp. usando a API ServiceLoader , mas que não se auto- registrou durante o desligamento do webapp. Esta mensagem é puramente informal, Tomcat já tomou a ação de prevenção de memory leaks em conformidade.

O que você pode fazer?

  1. Ignore esses avisos. Tomcat está fazendo o seu trabalho direito. O bug atual está no código de outra pessoa (o driver JDBC em questão), não no seu. Fique feliz que o Tomcat fez seu trabalho corretamente e espere até que o fornecedor do driver JDBC o conserte para que você possa atualizar o driver. Por outro lado, você não deveria abandonar um driver JDBC no /WEB-INF/lib , mas apenas no servidor /lib . Se você ainda o mantém no webapp /WEB-INF/lib , então você deve manualmente registrá-lo e cancelá-lo usando um ServletContextListener .

  2. Faça o downgrade para o Tomcat 6.0.23 ou mais antigo para que você não seja incomodado com esses avisos. Mas vai silenciosamente manter vazando memory. Não tenho certeza se é bom saber depois de tudo. Esse tipo de memory leaks é uma das principais causas por trás dos problemas do OutOfMemoryError durante os hotdots do Tomcat.

  3. Mova o driver JDBC para a pasta /lib do Tomcat e tenha uma origem de dados em pool de conexões para gerenciar o driver. Observe que o DBCP interno do Tomcat não desregistra os drivers adequadamente no fechamento. Veja também o bug DBCP-322 que é fechado como WONTFIX. Você preferiria replace o DBCP por outro pool de conexão que está fazendo seu trabalho melhor que o DBCP. Por exemplo, HikariCP , BoneCP ou talvez Tomcat JDBC Pool .

Em seu método contextDestroyed () do listener de contexto de servlet, cancele manualmente os drivers:

  // This manually deregisters JDBC driver, which prevents Tomcat 7 from complaining about memory leaks wrto this class Enumeration drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { Driver driver = drivers.nextElement(); try { DriverManager.deregisterDriver(driver); LOG.log(Level.INFO, String.format("deregistering jdbc driver: %s", driver)); } catch (SQLException e) { LOG.log(Level.SEVERE, String.format("Error deregistering driver %s", driver), e); } } 

Embora o Tomcat não cancele o driver JDBC forçadamente para você, não deixa de ser uma boa prática limpar todos os resources criados pelo seu aplicativo na destruição do contexto caso você migre para outro contêiner de servlet que não faz as verificações de prevenção de memory leaks que o Tomcat faz.

No entanto, a metodologia do cancelamento do registro do driver é perigosa. Alguns drivers retornados pelo método DriverManager.getDrivers() podem ter sido carregados pelo ClassLoader pai (ou seja, o classloader do servlet container) e não pelo ClassLoader do contexto do webapp (por exemplo, eles podem estar na pasta lib do contêiner, não no webapp e compartilhada em todo o contêiner). O cancelamento desses registros afetará quaisquer outras aplicações web que possam estar sendo usadas (ou até mesmo o próprio container).

Portanto, deve-se verificar se o ClassLoader de cada driver é o ClassLoader do webapp antes de cancelar o registro. Então, no método contextDestroyed () do seu ContextListener:

 public final void contextDestroyed(ServletContextEvent sce) { // ... First close any background tasks which may be using the DB ... // ... Then close any DB connection pools ... // Now deregister JDBC drivers in this context's ClassLoader: // Get the webapp's ClassLoader ClassLoader cl = Thread.currentThread().getContextClassLoader(); // Loop through all drivers Enumeration drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { Driver driver = drivers.nextElement(); if (driver.getClass().getClassLoader() == cl) { // This driver was registered by the webapp's ClassLoader, so deregister it: try { log.info("Deregistering JDBC driver {}", driver); DriverManager.deregisterDriver(driver); } catch (SQLException ex) { log.error("Error deregistering JDBC driver {}", driver, ex); } } else { // driver was not registered by the webapp's ClassLoader and may be in use elsewhere log.trace("Not deregistering JDBC driver {} as it does not belong to this webapp's ClassLoader", driver); } } } 

Eu vejo esta questão surgir muito. Sim, o Tomcat 7 automaticamente cancela o registro, mas isso REALMENTE assume o controle do seu código e uma boa prática de codificação? Certamente você quer saber que você tem todo o código correto para fechar todos os seus objects, encerrar os threads do pool de conexão ao database e se livrar de todos os avisos. Eu certamente faço.

É assim que eu faço.

Etapa 1: registrar um ouvinte

web.xml

  com.mysite.MySpecialListener  

Etapa 2: implementar o ouvinte

com.mysite.MySpecialListener.java

 public class MySpecialListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { // On Application Startup, please… // Usually I'll make a singleton in here, set up my pool, etc. } @Override public void contextDestroyed(ServletContextEvent sce) { // On Application Shutdown, please… // 1. Go fetch that DataSource Context initContext = new InitialContext(); Context envContext = (Context)initContext.lookup("java:/comp/env"); DataSource datasource = (DataSource)envContext.lookup("jdbc/database"); // 2. Deregister Driver try { java.sql.Driver mySqlDriver = DriverManager.getDriver("jdbc:mysql://localhost:3306/"); DriverManager.deregisterDriver(mySqlDriver); } catch (SQLException ex) { logger.info("Could not deregister driver:".concat(ex.getMessage())); } // 3. For added safety, remove the reference to dataSource for GC to enjoy. dataSource = null; } } 

Por favor, sinta-se livre para comentar e / ou adicionar …

Isso é puramente problema de registro / cancelamento de registro do driver no mysql`s driver ou tomcats webapp-classloader. Copie o driver mysql para a pasta lib do tomcats (então é carregado pelo jvm diretamente, não pelo tomcat), e a mensagem será removida. Isso faz com que o driver mysql jdbc seja descarregado apenas no desligamento da JVM, e ninguém se preocupa com vazamentos de memory.

Se você está recebendo esta mensagem de uma guerra construída pelo Maven, altere o escopo do driver JDBC para fornecido e coloque uma cópia dele no diretório lib. Como isso:

  mysql mysql-connector-java 5.1.18  provided  

Solução para implantações por aplicativo

Este é um ouvinte que escrevi para resolver o problema: ele detecta automaticamente se o driver se registrou e age de acordo.

Importante: ele deve ser usado SOMENTE quando o jar do driver estiver implementado no WEB-INF / lib , não no Tomcat / lib, como muitos sugerem, para que cada aplicativo possa cuidar de seu próprio driver e rodar em um Tomcat intocado . É assim que deve ser IMHO.

Basta configurar o ouvinte em seu web.xml antes de qualquer outro e aproveite.

adicione perto do topo do web.xml :

  utils.db.OjdbcDriverRegistrationListener  

salve como utils / db / OjdbcDriverRegistrationListener.java :

 package utils.db; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Enumeration; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import oracle.jdbc.OracleDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Registers and unregisters the Oracle JDBC driver. * * Use only when the ojdbc jar is deployed inside the webapp (not as an * appserver lib) */ public class OjdbcDriverRegistrationListener implements ServletContextListener { private static final Logger LOG = LoggerFactory .getLogger(OjdbcDriverRegistrationListener.class); private Driver driver = null; /** * Registers the Oracle JDBC driver */ @Override public void contextInitialized(ServletContextEvent servletContextEvent) { this.driver = new OracleDriver(); // load and instantiate the class boolean skipRegistration = false; Enumeration drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { Driver driver = drivers.nextElement(); if (driver instanceof OracleDriver) { OracleDriver alreadyRegistered = (OracleDriver) driver; if (alreadyRegistered.getClass() == this.driver.getClass()) { // same class in the VM already registered itself skipRegistration = true; this.driver = alreadyRegistered; break; } } } try { if (!skipRegistration) { DriverManager.registerDriver(driver); } else { LOG.debug("driver was registered automatically"); } LOG.info(String.format("registered jdbc driver: %sv%d.%d", driver, driver.getMajorVersion(), driver.getMinorVersion())); } catch (SQLException e) { LOG.error( "Error registering oracle driver: " + "database connectivity might be unavailable!", e); throw new RuntimeException(e); } } /** * Deregisters JDBC driver * * Prevents Tomcat 7 from complaining about memory leaks. */ @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { if (this.driver != null) { try { DriverManager.deregisterDriver(driver); LOG.info(String.format("deregistering jdbc driver: %s", driver)); } catch (SQLException e) { LOG.warn( String.format("Error deregistering driver %s", driver), e); } this.driver = null; } else { LOG.warn("No driver to deregister"); } } } 

Eu adicionarei a isso algo que encontrei nos fóruns do Spring. Se você mover o jar do driver JDBC para a pasta lib do tomcat, em vez de implantá-lo com o seu aplicativo da Web, o aviso parecerá desaparecer. Eu posso confirmar que isso funcionou para mim

http://forum.springsource.org/showthread.php?87335-Failure-to-unregister-the-MySQL-JDBC-Driver&p=334883#post334883

Descobri que implementar um método destroy () simples para cancelar o registro de qualquer driver JDBC funciona bem.

 /** * Destroys the servlet cleanly by unloading JDBC drivers. * * @see javax.servlet.GenericServlet#destroy() */ public void destroy() { String prefix = getClass().getSimpleName() +" destroy() "; ServletContext ctx = getServletContext(); try { Enumeration drivers = DriverManager.getDrivers(); while(drivers.hasMoreElements()) { DriverManager.deregisterDriver(drivers.nextElement()); } } catch(Exception e) { ctx.log(prefix + "Exception caught while deregistering JDBC drivers", e); } ctx.log(prefix + "complete"); } 

Eu estava tendo um problema semelhante, mas além disso eu estava recebendo um erro Java Heap Space sempre que eu modifiquei / salvou páginas JSP com o servidor Tomcat em execução, portanto, o contexto não foi totalmente recarregado.

Minhas versões foram Apache Tomcat 6.0.29 e JDK 6u12.

A atualização do JDK para 6u21, conforme sugerido na seção Referências do URL http://wiki.apache.org/tomcat/MemoryLeakProtection, resolveu o problema do espaço heap Java (o contexto agora é recarregado em OK), embora o erro do driver JDBC ainda apareça.

Eu encontrei o mesmo problema com o Tomcat versão 6.026.

Eu usei o Mysql JDBC.jar na Biblioteca WebAPP, assim como no TOMCAT Lib.

Para corrigir o problema acima, remova o Jar da pasta lib do TOMCAT.

Então, o que eu entendo é que o TOMCAT está manipulando o memory leaks JDBC corretamente. Mas se o jar MYSQL Jdbc for duplicado no WebApp e no Tomcat Lib, o Tomcat só conseguirá manipular o jar presente na pasta Lib do Tomcat.

Eu enfrentei esse problema quando estava implantando meu aplicativo Grails na AWS. Esta é a questão do driver org.h2 do driver padrão JDBC. Como você pode ver isso no Datasource.groovy dentro da sua pasta de configuração. Como você pode ver abaixo:

 dataSource { pooled = true jmxExport = true driverClassName = "org.h2.Driver" // make this one comment username = "sa" password = "" } 

Comente as linhas onde quer que seja mencionado org.h2.Driver no arquivo datasource.groovy, se você não estiver usando esse database. Caso contrário, você precisará fazer o download desse arquivo jar do database.

Obrigado .

Para evitar esse memory leaks, basta cancelar o registro do driver no desligamento do contexto.

pom.xml

 < ?xml version="1.0" encoding="UTF-8"?>  4.0.0 com.mywebsite emusicstore 1.0-SNAPSHOT    org.apache.maven.plugins maven-compiler-plugin 3.7.0  1.9 1.9        org.hibernate hibernate-core 4.0.1.Final   org.hibernate.javax.persistence hibernate-jpa-2.0-api 1.0.1.Final    mysql mysql-connector-java 8.0.11    javax.servlet servlet-api 2.5 provided    

MyWebAppContextListener.java

 package com.emusicstore.utils; import com.mysql.cj.jdbc.AbandonedConnectionCleanupThread; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Enumeration; public class MyWebAppContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent servletContextEvent) { System.out.println("************** Starting up! **************"); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { System.out.println("************** Shutting down! **************"); System.out.println("Destroying Context..."); System.out.println("Calling MySQL AbandonedConnectionCleanupThread checkedShutdown"); AbandonedConnectionCleanupThread.checkedShutdown(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); Enumeration drivers = DriverManager.getDrivers(); while (drivers.hasMoreElements()) { Driver driver = drivers.nextElement(); if (driver.getClass().getClassLoader() == cl) { try { System.out.println("Deregistering JDBC driver {}"); DriverManager.deregisterDriver(driver); } catch (SQLException ex) { System.out.println("Error deregistering JDBC driver {}"); ex.printStackTrace(); } } else { System.out.println("Not deregistering JDBC driver {} as it does not belong to this webapp's ClassLoader"); } } } } 

web.xml

 < ?xml version="1.0" encoding="UTF-8"?>   com.emusicstore.utils.MyWebAppContextListener    

Fonte que me inspirou para essa correção de bug.

Removendo o aplicativo (tomcat6) resolve isso. Os arquivos conf são preservados. Isso se quebra de alguma forma. Não sei bem como isso acontece.