当我运行我的web应用程序时,我得到这条消息。它运行良好,但我在关机期间收到这条消息。

严重:web应用程序注册了JBDC驱动程序[oracle.jdbc.driver. exe]。但是当web应用程序停止时,无法注销它。为了防止内存泄漏,JDBC驱动程序已被强制注销。

感谢任何帮助。


当前回答

要防止这种内存泄漏,只需在上下文关闭时注销驱动程序。

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>

这个bug修复的灵感来源。

其他回答

我在Tomcat版本6.026中发现了同样的问题。

我在WebAPP库和TOMCAT库中使用Mysql JDBC.jar。

要解决上述问题,请从TOMCAT lib文件夹中删除Jar。

因此,我所理解的是TOMCAT正在正确地处理JDBC内存泄漏。但是如果MYSQL Jdbc jar在WebApp和Tomcat Lib中被复制,Tomcat将只能处理Tomcat Lib文件夹中的jar。

我将补充一些我在Spring论坛上发现的东西。如果您将JDBC驱动程序jar移到tomcat lib文件夹,而不是与webapp一起部署它,则警告似乎消失了。我可以确定这对我很有效

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

我经常看到这个问题。是的,Tomcat 7会自动注销它,但这真的能控制你的代码吗?这是一个好的编码实践吗?当然,您希望知道您已经准备好了关闭所有对象、关闭数据库连接池线程和消除所有警告所需的所有正确代码。我当然喜欢。

我就是这么做的。

步骤1:注册监听器

web . xml

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

步骤2:实现监听器

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

}

请随意评论和/或添加…

在你的servlet上下文监听器contextDestroyed()方法中,手动注销驱动程序:

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

从版本6.0.24开始,Tomcat附带了内存泄漏检测特性,当web应用程序的/WEB-INF/lib中有JDBC 4.0兼容的驱动程序时,该驱动程序会在web应用程序启动期间使用ServiceLoader API自动注册自己,但在web应用程序关闭期间不会自动注销自己,这会导致此类警告消息。此消息完全是非正式的,Tomcat已经采取了相应的内存泄漏预防操作。

你能做什么?

Ignore those warnings. Tomcat is doing its job right. The actual bug is in someone else's code (the JDBC driver in question), not in yours. Be happy that Tomcat did its job properly and wait until the JDBC driver vendor get it fixed so that you can upgrade the driver. On the other hand, you aren't supposed to drop a JDBC driver in webapp's /WEB-INF/lib, but only in server's /lib. If you still keep it in webapp's /WEB-INF/lib, then you should manually register and deregister it using a ServletContextListener. Downgrade to Tomcat 6.0.23 or older so that you will not be bothered with those warnings. But it will silently keep leaking memory. Not sure if that's good to know after all. Those kind of memory leaks are one of the major causes behind OutOfMemoryError issues during Tomcat hotdeployments. Move the JDBC driver to Tomcat's /lib folder and have a connection pooled datasource to manage the driver. Note that Tomcat's builtin DBCP does not deregister drivers properly on close. See also bug DBCP-322 which is closed as WONTFIX. You would rather like to replace DBCP by another connection pool which is doing its job better then DBCP. For example HikariCP or perhaps Tomcat JDBC Pool.