java - 独立的 jdbc-pool 实现内存泄漏

标签 java sql-server tomcat connection-pooling tomcat7

我正在尝试在独立的 Web 应用程序(自包含 - 不依赖于 server.xml)中实现 jdbc-pool,以便它可以移动到可能早于 7.0 的 tomcat 安装。

我正在使用 sourceforge 驱动程序 (net.sourceforge.jtds.jdbc.Driver) 连接到 MSSQL Server

除此错误外一切正常:

SEVERE: The web application [/jdbc-pool] appears to have started a thread named [[Pool-Cleaner]:Tomcat Connection Pool[1-12524859]] but has failed to stop it. This is very likely to create a memory leak.

基于 this我确定我需要关闭 jdbc-pool 数据源。不过,我在处理该帖子的最后一行时遇到了问题:

>> If it is configured in the application context, then this simply means you forgot to call DataSource.close on the connection pool when your web application is stopped.

> This is confusing advice because javax.sql.DataSource doesn't have a close() method.

In order to call close, one has to cast it to what ever the data source you are using.

如何找出我正在使用的数据源类型以及它的类在哪里?我能以某种方式从驱动程序 jar 中提取它吗?

除了使用池的 servlet 之外,我还使用了 ServletContextListener,这样我就可以立即从 contextInitialized 方法开始使用池连接。我开始添加代码以终止此 ServletContextListener 的 contextDestroyed 方法中的连接,但在问号处挂断了:

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;

public class JdbcPoolListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent myServletContextEvent) {

        // initialize jdbc-pool datasource to start out with pooled connections 
        try {
            Context myContext = (Context) new InitialContext().lookup("java:comp/env");
            DataSource myDataSource = (DataSource) myContext.lookup("jdbc/db");
            myServletContextEvent.getServletContext().setAttribute("JdbcPool", myDataSource);
        } catch (NamingException e) {
            System.out.println("Error initializing jdbc-pool datasource");
            e.printStackTrace();
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent myServletContextEvent) {

        // failed attempt to close the data source
        ServletContext myServletContext = myServletContextEvent.getServletContext();
        //DataSource myDataSource = (DataSource) myServletContext.getAttribute("JdbcPool");
        DataSource dataSource = (DataSource)((???) myServletContext.getAttribute(contextAttribute)).getConfiguration().getEnvironment().getDataSource();
        dataSource.close();
        myServletContext.removeAttribute("JdbcPool");

        // deregister JDBC driver to prevent Tomcat 7 from complaining about memory leaks
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                System.out.println(String.format("Deregistering jdbc driver: %s", driver));
            } catch (SQLException e) {
                System.out.println(String.format("Error deregistering driver %s", driver));
                e.printStackTrace();
            }
        }
    }
}

最佳答案

解决了这个问题,我注意到 tomcat.jdbc.pool.DataSourceProxy 有一个 close 方法,所以我将数据源转换为 DataSourceProxy,然后对其调用 closed .我现在不再在日志中收到 tomcat 内存泄漏错误。

解决方案:

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;

import org.apache.tomcat.jdbc.pool.DataSourceProxy;

public class JdbcPoolListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent myServletContextEvent) {

        // initialize jdbc-pool datasource to start out with pooled connections 
        try {
            Context myContext = (Context) new InitialContext().lookup("java:comp/env");
            DataSource myDataSource = (DataSource) myContext.lookup("jdbc/cf");
            myServletContextEvent.getServletContext().setAttribute("JdbcPool", myDataSource);
        } catch (NamingException e) {
            System.out.println("Error initializing jdbc-pool datasource");
            e.printStackTrace();
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent myServletContextEvent) {

    // close datasource from proxy?
    ServletContext myServletContext = myServletContextEvent.getServletContext();
        DataSourceProxy myDataSource = (DataSourceProxy) myServletContext.getAttribute("JdbcPool");
        myDataSource.close();       
        myServletContext.removeAttribute("JdbcPool");

        // deregister JDBC driver to prevent Tomcat 7 from complaining about memory leaks
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                System.out.println(String.format("Deregistering jdbc driver: %s", driver));
            } catch (SQLException e) {
                System.out.println(String.format("Error deregistering driver %s", driver));
                e.printStackTrace();
            }
        }
    }
}

关于java - 独立的 jdbc-pool 实现内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8903379/

相关文章:

java - 如何在 MySQL 中创建临时过程?

Java 使用内线程是否明智?如果是,如何提取异常?

java - 在代码中检测 PDF 包或组合

java - 我应该为具有大量必需属性的类创建一个构建器吗

mysql - 从 Microsoft SQL 背景学习 MySql

c# - 隐藏 Id 属性时自定义 IdentityRole<TKey, TUserRole> 失败

java - Red Hat Tomcat 7容器上java的Heapsize等内存配置

apache - 如何从 cpanel 卸载 apache 服务器?

jakarta-ee - servlet : "FileNotFoundException: log4j.properties"中log4j的配置

sql - 当列未知时,使用 count() 和 CTE 进行空值动态透视