18

I have a problem in tomcat 7, and here are some info about it,

1 - I have this message:

INFO: Reloading Context with name [/WebApp] has started
Oct 04, 2013 12:20:50 PM org.apache.catalina.loader.WebappClassLoader clearReferencesJdbc
SEVERE: The web application [/WebApp] 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.

Oct 04, 2013 12:20:50 PM org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [/WebApp] appears to have started a thread named [Abandoned connection cleanup thread] but has failed to stop it. This is very likely to create a memory leak.
Oct 04, 2013 12:20:51 PM org.apache.catalina.core.StandardContext reload
INFO: Reloading Context with name [/WebApp] is completed

2 - When I reload the application the problem solved for about 20 hours then comes back again.

3 - I have about 10 application deployed on the tomcat but just 2 of them gets this error.

4 - the problem was not exist from the begging with these 2 apps but appeared from about 2 weeks.

So how can I solve this and is it related to my code?

Rubens Mariuzzo
  • 28,358
  • 27
  • 121
  • 148
AAH
  • 301
  • 1
  • 2
  • 6
  • 3
    Because the driver should ideally be in Tomcat's classpath rather than the webapp's. If the driver is in the webapp's classpath then not unregistering it will cause a perm-gen leak as the `ClassLoader` cannot be unloaded. – Boris the Spider Oct 04 '13 at 19:44
  • @Boris There's no difference between Tomcat's classpath and the application's. It's a single `java` process. – Sotirios Delimanolis Oct 04 '13 at 19:48
  • @Boris ok I will assume that, but as I said I have 10 apps deployed, and all have the driver added to it's path, so why the error did not appear for all apps? – AAH Oct 04 '13 at 19:50
  • @SotiriosDelimanolis don't be silly. It might help if you read [this](http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html). In short each webapp has it's own classpath consisting of the `lib` folder in the webapp. – Boris the Spider Oct 04 '13 at 19:53
  • @BoristheSpider My point was to use the terminology carefully. There's only one `classpath` and that's the Tomcat's. Just like it says in the document you linked, we should say your application's `ClassLoader`. – Sotirios Delimanolis Oct 04 '13 at 19:57
  • 1
    @SotiriosDelimanolis You are merely arguing semantics, though the use of the term "classpath" is a poor choice all-around. If you replace the term "classpath" with "class loader" then all is well except for your nit-picky comments. Instead of simply telling people they are wrong (and not helping in the slightest), perhaps you should correct the terminology instead. – Christopher Schultz Oct 04 '13 at 20:15
  • Possible duplicate of http://stackoverflow.com/questions/3320400/to-prevent-a-memory-leak-the-jdbc-driver-has-been-forcibly-unregistered – James Jenkins Jan 14 '14 at 13:18
  • Check this link. http://stackoverflow.com/questions/6981564/why-jdbc-driver-must-been-put-in-tomcat-home-lib-folder it is working for me too – Abhishek Chatterjee Jun 20 '14 at 10:16

2 Answers2

20

When you stop a web application in Tomcat, it tries to shutdown the threads it started and closes a bunch of resources, for example the JDBC drivers. Although in this case it is capable of closing them, it's safer to do it yourself.

You can do this in a ServletContextListener. I've implemented mine as follows

@WebListener // register it as you wish
public class ContainerContextClosedHandler implements ServletContextListener {
    private static final Logger logger = LoggerFactory.getLogger(ContainerContextClosedHandler.class);

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        // nothing to do
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        Enumeration<Driver> drivers = DriverManager.getDrivers();     

        Driver driver = null;

        // clear drivers
        while(drivers.hasMoreElements()) {
            try {
                driver = drivers.nextElement();
                DriverManager.deregisterDriver(driver);

            } catch (SQLException ex) {
                // deregistration failed, might want to do something, log at the very least
            }
        }

        // MySQL driver leaves around a thread. This static method cleans it up.
        try {
            AbandonedConnectionCleanupThread.shutdown();
        } catch (InterruptedException e) {
            // again failure, not much you can do
        }
    }

}

MySQL does starts a Thread that Tomcat cannot close. For current versions (5.1.23+), they've provided the AbandonedConnectionCleanupThread class to close the spawned Thread, as you can see above.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 1
    +1. `Collections.list(DriverManager.getDrivers()).forEach(d->{try {DriverManager.deregisterDriver(d);}catch(final SQLException sqle){}});` – Jin Kwon Nov 19 '14 at 08:58
  • To avoid such problems you can use database connection pool on server. The same problem was on Glassfish + HyperSQL. – Ernestas Gruodis Mar 02 '16 at 21:11
8

If you have your Connector/J JDBC driver in each webapp's WEB-INF/lib directory, then you will likely have similar problems with all of your webapps -- not just this one.

If you are using Tomcat's JDBC connection pool, then you should put the Connector/J driver into Tomcat's lib/ directory and remove it from all of your webapps. If you are maintaining your own connection pool from within your own application, then you will have to arrange for the JDBC driver to be de-register itself with the global DriverManager. Better yet, use Connector/J's non-registering driver instead of the registering driver and then you don't have to worry about these kinds of leaks that Tomcat is actually protecting you from.

Christopher Schultz
  • 20,221
  • 9
  • 60
  • 77
  • Say you declare a resource in Tomcat's configuration and no live web application is using it, does Tomcat shut it down? – Sotirios Delimanolis Oct 04 '13 at 20:23
  • Tomcat creates the resource on startup but doesn't make any connections initially. If you never use it, it will use minimal resources. IIRC, once you use it, those connections will remain open indefinitely, even if the webapp using them is undeployed. You can set "idle timeouts" for the connections and then they will be closed after that interval. – Christopher Schultz Oct 07 '13 at 16:11
  • In this case, you would still need to call `shutdown()` on the `AbandonedConnectionCleanupThread` started by the MySQL JDBC driver. – Sotirios Delimanolis Oct 07 '13 at 16:22
  • 1
    @SotiriosDelimanolis Yes, but that is a MySQL Connector/J-specific thing. I've been complaining to the MySQL that their driver does stupid things with regard to the `ClassLoader`, threads, etc. but they don't seem to care. We have to work-around that stupidity to have webapps cleanly undeploy. – Christopher Schultz Oct 07 '13 at 16:25