기술과 산업/언어 및 프레임워크

Tomcat에서 DBCP 사용할 때 Memory leak

B컷개발자 2014. 5. 9. 12:40
728x90

Problem Domain


registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered. 


 - dbcp, boneCP를 사용하던... 에러는 발생한다.


 - mySQL, Oracle 관계 없이 에러는 발생한다.


 - JPA(+ Hibernate)를 사용할 때만 에러가 발생한다. (JPA를 사용하지 않을 때는 에러가 발생하지 않는다.)

    : org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean 및 관련 클래스를 등록하여

      사용할 경우 (나만그런가?)



Solve


해결 방법... google.com과 stackoverflow.com에서 방황을...-_-;;


1. DataSource가 Close 되는 시점에 명시적으로 드라이버를 unregister 시켜준다.


- extended BasicDataSource class

public class DisposableBasicDataSource extends BasicDataSource { 

@Override

public synchronized void close() throws SQLException {

DriverManager.deregisterDriver(DriverManager.getDriver(url));

super.close();

}

}


- added listener

public class JdbcDriverRegistrationListener implements ServletContextListener {

    /**

     * Default constructor. 

     */

    public JdbcDriverRegistrationListener() {

        // TODO Auto-generated constructor stub

    }


/**

     * @see ServletContextListener#contextDestroyed(ServletContextEvent)

     */

    public void contextDestroyed(ServletContextEvent sce)  {

    /**

    * simple

    */

    /*

    Enumeration<Driver> drivers = DriverManager.getDrivers();

        while (drivers.hasMoreElements()) {

            Driver driver = drivers.nextElement();

            try {

                DriverManager.deregisterDriver(driver);

                log.info(String.format("deregistering jdbc driver: %s", driver));

            } catch (SQLException e) {

            log.info(String.format("Error deregistering driver %s", driver), e);

            }

        }

        */

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

            }

        }

    } 

    /**

     * @see ServletContextListener#contextInitialized(ServletContextEvent)

     */

    public void contextInitialized(ServletContextEvent sce)  { 

         // TODO Auto-generated method stub

    }

}




2. 톰켓 lib 폴더에 jdbc 드라이버 jar를 복사한다.


3. JNDI를 사용한다.



컨테이너 리로드가 필요없는 서버는 상관없음.


톰켓에서만 나는건지 아니면 다른 WAS도 그런건지 잘 모르겠음.

728x90