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

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

感谢任何帮助。


当前回答

我经常看到这个问题。是的,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;
    }

}

请随意评论和/或添加…

其他回答

我也遇到了类似的问题,但除此之外,每当我在运行Tomcat服务器的情况下修改/保存JSP页面时,我都会得到一个Java堆空间错误,因此上下文没有完全充电。

我的版本是Apache Tomcat 6.0.29和JDK 6u12。

根据URL http://wiki.apache.org/tomcat/MemoryLeakProtection的参考部分的建议,将JDK升级到6u21解决了Java堆空间问题(上下文现在重新加载OK),尽管仍然出现JDBC驱动程序错误。

尽管Tomcat会强制注销JDBC驱动程序,但在上下文破坏时清理webapp创建的所有资源是一个很好的实践,以防您移动到另一个servlet容器,该容器不像Tomcat那样执行内存泄漏预防检查。

但是,全面取消司机登记的方法是危险的。DriverManager.getDrivers()方法返回的一些驱动程序可能是由父类加载器(即servlet容器的类加载器)加载的,而不是webapp上下文的类加载器(例如,它们可能在容器的lib文件夹中,而不是webapp的,因此在整个容器中共享)。注销这些将会影响其他可能使用它们的web应用程序(甚至容器本身)。

因此,在取消注册之前,应该检查每个驱动程序的ClassLoader是否是web应用程序的ClassLoader。因此,在你的ContextListener的contextDestroyed()方法中:

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

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

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

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

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

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

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

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

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修复的灵感来源。