353 Stimmen

Um ein Speicherleck zu vermeiden, wurde die Registrierung des JDBC-Treibers zwangsweise aufgehoben

Ich erhalte diese Meldung, wenn ich meine Webanwendung ausführe. Sie läuft gut, aber ich erhalte diese Meldung beim Herunterfahren.

SEVERE: Eine Webanwendung registrierte den JBDC-Treiber [oracle.jdbc.driver.OracleDriver], hob die Registrierung aber nicht auf, als die Webanwendung beendet wurde. Um ein Speicherleck zu vermeiden, wurde der JDBC-Treiber zwangsweise deregistriert.

Jede Hilfe ist willkommen.

322voto

BalusC Punkte 1034465

Seit Version 6.0.24 wird Tomcat mit einem Erkennung von Speicherlecks Funktion, die wiederum zu dieser Art von Warnmeldungen führen kann, wenn ein JDBC 4.0 kompatibler Treiber in der Webapps /WEB-INF/lib die auto- Register selbst während des Starts der Webapp unter Verwendung der ServiceLoader API die sich aber nicht auto- Deregistrierung selbst während des Herunterfahrens der Webapp. Diese Meldung ist rein informell, Tomcat hat bereits die entsprechenden Maßnahmen zur Vermeidung von Speicherlecks ergriffen.

Was können Sie tun?

  1. Ignorieren Sie diese Warnungen. Tomcat macht seine Arbeit richtig. Der eigentliche Fehler liegt im Code eines anderen (dem fraglichen JDBC-Treiber), nicht in Ihrem. Seien Sie froh, dass Tomcat seine Arbeit richtig gemacht hat, und warten Sie, bis der Hersteller des JDBC-Treibers den Fehler behoben hat, damit Sie den Treiber aktualisieren können. Andererseits sollten Sie einen JDBC-Treiber nicht in die Webapps /WEB-INF/lib aber nur im Serverbereich /lib . Wenn Sie es immer noch in webapp's behalten /WEB-INF/lib verwenden, dann sollten Sie es manuell registrieren und deregistrieren, indem Sie eine ServletContextListener .

  2. Führen Sie ein Downgrade auf Tomcat 6.0.23 oder älter durch, damit Sie nicht mit diesen Warnungen belästigt werden. Aber es wird weiterhin Speicherverluste geben. Ich bin mir nicht sicher, ob das nicht doch gut zu wissen ist. Diese Art von Speicherlecks sind eine der Hauptursachen für OutOfMemoryError Ausgaben während der Hotdeployments von Tomcat.

  3. Verschieben Sie den JDBC-Treiber in Tomcats /lib und verfügen über eine Connection-Pooling-Datenquelle zur Verwaltung des Treibers. Beachten Sie, dass Tomcat's eingebautes DBCP die Treiber beim Beenden nicht richtig deregistriert. Siehe auch Fehler DBCP-322 die als WONTFIX geschlossen wird. Sie möchten DBCP lieber durch einen anderen Verbindungspool ersetzen, der seine Aufgabe besser erfüllt als DBCP. Zum Beispiel HikariCP oder vielleicht Tomcat JDBC-Pool .

171voto

ae6rt Punkte 2648

Deregistrieren Sie die Treiber manuell in der Methode contextDestroyed() Ihres Servlet-Kontext-Listeners:

// 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);
    }
}

102voto

daiscog Punkte 10172

Obwohl Tomcat die Deregistrierung des JDBC-Treibers für Sie erzwingt, ist es dennoch eine gute Praxis, alle von Ihrer Webapplikation erstellten Ressourcen bei der Kontextzerstörung zu bereinigen, für den Fall, dass Sie zu einem anderen Servlet-Container wechseln, der nicht die Prüfungen zur Vermeidung von Speicherlecks durchführt, die Tomcat durchführt.

Die Methode der pauschalen Abmeldung von Fahrern ist jedoch gefährlich. Einige Fahrer, die von der DriverManager.getDrivers() Methode kann vom übergeordneten ClassLoader (d.h. dem Classloader des Servlet-Containers) und nicht vom ClassLoader des Webapp-Kontextes geladen worden sein (z.B. kann es sein, dass sie sich im lib-Ordner des Containers und nicht im lib-Ordner der Webapp befinden und daher im gesamten Container gemeinsam genutzt werden). Eine Deregistrierung wirkt sich auf alle anderen Webapps aus, die sie möglicherweise verwenden (oder sogar auf den Container selbst).

Daher sollte man überprüfen, ob der ClassLoader für jeden Treiber der ClassLoader der Webapp ist, bevor man ihn deregistriert. Also, in der Methode contextDestroyed() Ihres ContextListeners:

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);
        }
    }
}

28voto

sparkyspider Punkte 12520

Diese Frage stellt sich häufig. Ja, Tomcat 7 deregistriert sie automatisch, aber ist das wirklich eine gute Programmierpraxis, wenn man die Kontrolle über seinen Code übernimmt? Sicherlich wollen Sie wissen, dass Sie den richtigen Code haben, um alle Ihre Objekte zu schließen, die Threads des Datenbankverbindungspools zu beenden und alle Warnungen loszuwerden. Das will ich auf jeden Fall.

So mache ich es.

Schritt 1: Registrieren eines Listeners

web.xml

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

Schritt 2: Implementierung des Listeners

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;
    }

}

Bitte zögern Sie nicht, zu kommentieren und/oder zu ergänzen...

14voto

sick old bastard Punkte 141

Es handelt sich hierbei um ein reines Treiberregistrierungs-/Deregistrierungsproblem im mysql-Treiber oder im tomcats webapp-classloader. Kopieren Sie den mysql-Treiber in den tomcats lib-Ordner (damit er direkt vom jvm geladen wird, nicht vom tomcat), und die Meldung wird verschwinden. Dadurch wird der mysql jdbc-Treiber nur beim Herunterfahren der JVM entladen, und niemand kümmert sich dann um Speicherlecks.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X