Pour éviter une fuite de mémoire, le pilote JDBC a été forcé de ne pas être enregistré.

je reçois ce message lorsque j'exécute mon application web. Il fonctionne très bien mais je reçois ce message pendant l'arrêt.

sévère: une application web enregistré le pilote JBDC [oracle.jdbc.pilote.OracleDriver] mais n'a pas réussi à le désinscrire lorsque l'application web a été arrêtée. Pour éviter une fuite de mémoire, le pilote JDBC n'a pas été enregistré de force.

toute aide appréciée.

287
demandé sur BalusC 2010-07-23 20:45:16

14 réponses

depuis la version 6.0.24, Tomcat bateaux avec un détection de fuite de mémoire fonctionnalité, qui à son tour peut conduire à ce genre de messages d'avertissement quand il ya un JDBC 4.0 pilote compatible dans la webapp /WEB-INF/lib qui AUTO- enregistre lui - même pendant le démarrage de webapp en utilisant le ServiceLoader API , mais qui n'a pas auto - déréglementer lui-même lors de l'arrêt de webapp. Ce message est purement informel, Tomcat a déjà pris les mesures de prévention des fuites de mémoire en conséquence.

Que pouvez-vous faire?

  1. ignorez ces avertissements. Tomcat fait bien son travail. Le bug réel est dans le code de quelqu'un d'autre (le pilote JDBC en question), pas dans le vôtre. Soyez heureux que Tomcat ait fait son travail correctement et attendez que le fournisseur de pilotes JDBC le fasse réparer pour que vous puissiez mettre à niveau le pilote. D'autre part, vous n'êtes pas censé tomber un pilote JDBC dans /WEB-INF/lib de webapp , mais seulement dans /lib du serveur . Si vous le conservez dans le /WEB-INF/lib de webapp, vous devez l'enregistrer et le désenregistrer manuellement en utilisant un ServletContextListener .

  2. déclassement à Tomcat 6.0.23 ou plus afin que vous ne soyez pas dérangé par ces avertissements. Mais il continuera silencieusement à perdre la mémoire. Vous ne savez pas si c'est bon à savoir après tout. Ce genre de fuites de mémoire sont une des principales causes derrière OutOfMemoryError questions au cours de Tomcat hotdeployments.

  3. déplacer le pilote JDBC dans le dossier /lib de Tomcat et avoir une source de données commune de connexion pour gérer le pilote. Notez que le DBCP intégré de Tomcat ne déréglemente pas correctement les pilotes sur close. Voir Aussi Le bogue DBCP-322 qui est fermé en tant que WONTFIX. Vous souhaitez remplacer DBCP par un autre pool de connexion qui fait son travail mieux que DBCP. Par exemple HikariCP , BoneCP , ou peut-être Tomcat JDBC Pool .

274
répondu BalusC 2015-12-16 19:21:35

dans votre méthode servlet context listener contextDestroyed (), déréglementez manuellement les pilotes:

        // This manually deregisters JDBC driver, which prevents Tomcat 7 from complaining about memory leaks wrto this class
        Enumeration<Driver> 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);
            }

        }
156
répondu ae6rt 2011-03-15 17:20:48

bien que Tomcat déréglemente de force le pilote JDBC pour vous, il est néanmoins de bonne pratique de nettoyer toutes les ressources créées par votre webapp sur la destruction de contexte au cas où vous vous déplacez vers un autre conteneur servlet qui ne fait pas les vérifications de prévention des fuites de mémoire que Tomcat fait.

toutefois, la méthode de radiation générale des conducteurs est dangereuse. certains conducteurs retournés par la méthode DriverManager.getDrivers() peuvent avoir été chargés par le ClassLoader parent (i.e., le classloader du conteneur servlet) pas le ClassLoader du contexte webapp (i.e., ils peuvent être dans le dossier lib du conteneur, pas le webapp, et donc partagés sur l'ensemble du conteneur). Déréglementer ceux-ci affectera toute autre webapps qui peuvent être en train de les utiliser (ou même le conteneur lui-même).

par conséquent, il faut vérifier que le ClassLoader pour chaque pilote est le ClassLoader de la webapp avant de le déréglementer. Donc, dans votre ContextListener de contextDestroyed() la méthode:

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<Driver> 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);
        }
    }
}
75
répondu megaflop 2014-05-28 12:56:55

je vois cette question beaucoup. Oui, Tomcat 7 le déréglemente automatiquement, mais est-ce que cela prend vraiment le contrôle de votre code et une bonne pratique de codage? Vous voulez sûrement savoir que vous avez tout le code correct en place pour fermer tous vos objets, fermer les threads de connexion de base de données, et se débarrasser de tous les Avertissements. Je n'ai certainement.

C'est comme ça que je le fais.

Étape 1: enregistrer un auditeur

web.xml

<listener>
    <listener-class>com.mysite.MySpecialListener</listener-class>
</listener>

Étape 2: Mettre en œuvre l'auditeur

com.monsite.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;
    }

}

s'il vous Plaît n'hésitez pas à commenter et/ou d'ajouter...

24
répondu Spider 2016-09-30 10:46:09

il s'agit d'un problème purement lié à l'inscription et à la radiation des pilotes dans le driver de mysql ou tomcats webapp-classloader. Copiez le pilote mysql dans le dossier lib de tomcats (donc il est chargé par jvm directement, pas par tomcat), et le message sera parti. Cela rend mysql pilote jdbc à décharger seulement à l'arrêt JVM, et personne ne se soucie des fuites de mémoire alors.

14
répondu sick old bastard 2013-07-31 02:43:55

si vous recevez ce message d'une guerre construite par Maven, changez la portée du pilote JDBC en Fourni, et mettez-en une copie dans le répertoire lib. Comme ceci:

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.18</version>
  <!-- put a copy in /usr/share/tomcat7/lib -->
  <scope>provided</scope>
</dependency>
8
répondu TimP 2012-09-13 10:15:11

Solution pour chaque application déploiements

c'est un auditeur que j'ai écrit pour résoudre le problème: il autodétecte si le pilote s'est enregistré et agit en conséquence.it

Important: il est destiné à être utilisé seulement lorsque le driver jar est déployé dans le WEB-INF/lib , pas dans le Tomcat /lib, comme beaucoup le suggèrent, afin que chaque application puisse prendre soin de son propre pilote et fonctionner sur un Tomcat intact. C'est la façon dont il devrait être À mon humble avis.

il suffit de configurer l'auditeur dans votre web.xml avant tout autre et profiter.

ajouter près du haut de "1519130920 web".xml :

<listener>
    <listener-class>utils.db.OjdbcDriverRegistrationListener</listener-class>    
</listener>

enregistrer sous 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<Driver> 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: %s v%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");
        }

    }

}
8
répondu Andrea Ratto 2013-04-04 16:25:55

je vais ajouter à cela quelque chose que j'ai trouvé sur les forums de printemps. Si vous déplacez votre pot de pilotes JDBC dans le dossier tomcat lib, au lieu de le déployer avec votre webapp, l'avertissement semble disparaître. Je peux confirmer que cela a fonctionné pour moi

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

6
répondu Collin Peters 2011-07-04 22:00:04

j'ai trouvé que la mise en œuvre d'une méthode de destroy() simple pour désinscrire les pilotes JDBC fonctionne bien.

/**
 * 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<Driver> 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");
}
6
répondu Darrin M. Gorski 2012-02-28 19:05:58

j'avais un problème similaire, mais en plus j'avais une erreur D'espace de tas Java à chaque fois que je modifiais/enregistrais des pages JSP avec Tomcat server en cours d'exécution, donc le contexte n'était pas complètement rechargé.

mes versions étaient Apache Tomcat 6.0.29 et JDK 6u12.

mise à niveau du JDK 6u21 comme suggéré dans la section Références "151960920 de la section" URL http://wiki.apache.org/tomcat/MemoryLeakProtection a résolu le problème D'Espace Java Heap (le contexte recharge maintenant OK) bien que L'erreur du pilote JDBC apparaisse toujours.

2
répondu Francisco Alvarado 2010-09-13 22:02:17

j'ai trouvé le même problème avec Tomcat version 6.026.

j'ai utilisé le Mysql JDBC.jar dans WebAPP de la Bibliothèque ainsi que dans la Lib de TOMCAT.

pour corriger ce qui précède en retirant le bocal du dossier TOMCAT lib.

donc ce que je comprends C'est que TOMCAT gère correctement la fuite de mémoire JDBC. Mais si le JAR MYSQL Jdbc est dupliqué dans WebApp et Tomcat Lib, Tomcat ne pourra traiter que le jar présent dans la Lib Tomcat. dossier.

0
répondu Bharat 2012-04-24 16:03:09

j'ai fait face à ce problème lorsque j'ai déployé mon application Grails sur AWS. Il s'agit du pilote par défaut JDBC org.h2 "151930920 pilote". Comme vous pouvez le voir dans la source de données.groovy à l'intérieur de votre dossier de configuration . Comme vous pouvez le voir ci-dessous :

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

Commenter ces lignes là où est mentionné org.h2.Pilote dans la source de données.groovy fichier, si vous n'utilisez pas cette base de données . Sinon, vous télécharger la base de données de fichier jar .

Merci .

0
répondu PyDevSRS 2017-01-08 17:21:17

pour prévenir cette fuite de mémoire, il suffit de déréglementer le conducteur lors de l'arrêt du contexte.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mywebsite</groupId>
    <artifactId>emusicstore</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>1.9</source>
                    <target>1.9</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!-- ... -->

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.0.1.Final</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.0-api</artifactId>
            <version>1.0.1.Final</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/javax.servlet/servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

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<Driver> 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"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <listener>
        <listener-class>com.emusicstore.utils.MyWebAppContextListener</listener-class>
    </listener>

<!-- ... -->

</web-app>

Source qui m'a inspiré pour cette correction de bug.

0
répondu King-Wizard 2018-05-01 17:34:49

Supprimer l'application (tomcat6) résout le problème. Les fichiers conf sont conservés. Il se casse d'une façon ou d'une autre. Je ne suis pas vraiment sûr de comment ça marche.

-10
répondu Ivan Georgiev 2011-08-31 16:33:58