Tomcat에서 DBCP 사용할 때 Memory leak
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도 그런건지 잘 모르겠음.