утечка памяти в автономной реализации jdbc-pool

Я пытаюсь реализовать jdbc-pool в отдельном веб-приложении (самостоятельно - не полагаясь на server.xml), чтобы его можно было переместить в установки tomcat, которые могут быть раньше, чем 7.0.

Я подключаюсь к MSSQL Server с помощью драйвера sourceforge (net.sourceforge.jtds.jdbc.Driver)

Все работает нормально, кроме этой ошибки:

СЕРЬЕЗНЫЙ: веб-приложение [/jdbc-pool], похоже, запустило поток с именем [[Pool-Cleaner]:Tomcat Connection Pool[1-12524859]], но не смогло его остановить. Это очень вероятно, чтобы создать утечку памяти.

На основе этого Я решил, что мне нужно закрыть источник данных jdbc-pool. Однако у меня возникают проблемы с этой последней строкой из этого сообщения:

>> Если он настроен в контексте приложения, это просто означает, что вы забыли вызвать DataSource.close в пуле соединений, когда ваше веб-приложение остановлено.

> Это сбивает с толку совет, потому что javax.sql.DataSource не имеет метода close().

Чтобы вызвать close, нужно привести его к тому источнику данных, который вы используете.

Как узнать, какой тип источника данных я использую и где находится класс для него? Могу ли я как-то извлечь его из банки с драйверами?

В дополнение к сервлету, который использует пул, я использую ServletContextListener, так что я могу начать с соединений в пуле сразу из метода contextInitialized. Я начал добавлять код для уничтожения соединения в методе contextDestroyed этого ServletContextListener, но завис там, где стоят вопросительные знаки:

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

person egerardus    schedule 17.01.2012    source источник
comment
в вашем конкретном случае это, скорее всего, net.sourceforge.jtds.jdbcx.JtdsDataSource и попробуйте поиграть с настройками socketTimeout, это закроет соединение, если оно простаивает. подробнее в документах драйвера.   -  person Sergey Benner    schedule 18.01.2012


Ответы (1)


Решив это, я заметил, что у tomcat.jdbc.pool.DataSourceProxy есть метод close, поэтому я привел источник данных как DataSourceProxy, а затем вызвал его close. Я больше не получаю ошибку утечки памяти 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();
            }
        }
    }
}
person egerardus    schedule 18.01.2012
comment
Я предполагаю, что если бы вы использовали класс org.apache.tomcat.jdbc.pool.DataSource, вы бы получили тот же результат, потому что он расширяет DataSourceProxy, а DataSourceProxy реализует конфигурацию пула, а DataSourceProxy имеет метод close() для закрытия пула. - person Sergey Benner; 18.01.2012
comment
@Sergey Benner Спасибо за разъяснение - person egerardus; 18.01.2012