1

At the moment I am writing a Spring and JDBC based web-application that interacts with a MySQL database on a Windows Server 2012 x64. The MySQL database driver is registered by Tomcat and not by the servlet itself (As so many people do).

Although I close all connection/prepared statements/result sets, Tomcat logs a possible memory leak during a restart and I don't know why. Additionally the server crashes (Read: times out) after a few days, maybe because the leaks.

$APACHE_DIR/conf/context.xml:

<?xml version='1.0' encoding='utf-8'?>
<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
    <Resource name="jdbc/testservlet" 
              global="jdbc/testservlet"
              type="javax.sql.DataSource"              
              username="root"
              password="123456aA"
              driverClassName="com.mysql.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/testservlet"
              maxTotal="10"
              maxIdle="10"
              maxWaitMillis="10000"
              removeAbandonedTimeout="300"            
              defaultAutoCommit="true" />
</Context>

$APACHE_DIR/webapps/testservlet/META-INF/context.xml:

<?xml version='1.0' encoding='utf-8'?>
<Context>
    <ResourceLink name="jdbc/testservlet" global="jdbc/testservlet" type="javax.sql.DataSource" />
</Context>

$PROJECT_DIR/DatabaseConfiguration.java

package com.example.testservlet.configuration;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DatabaseConfiguration
{
    @Bean
    public DataSource getDataSource() throws Exception
    {
        Context initialContext = new InitialContext();
        Context environmentContext = (Context) initialContext.lookup("java:comp/env");
        DataSource dataSource = (DataSource) environmentContext.lookup("jdbc/testservlet");
        return dataSource;
    }
}

$PROJECT_DIR/HomeController.java

package com.example.testservlet.controller;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HomeController
{
    @Autowired
    DataSource datasource;

    @ResponseBody
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public void home() throws Exception
    {
        Connection connection = null;
        ResultSet resultset = null;
        try
        {
            connection = datasource.getConnection();
            DatabaseMetaData metadata = connection.getMetaData();
            resultset = metadata.getTables(null, null, "%", null);
            while(resultset.next())
            {
              System.out.println("Table: " + resultset.getString(3));
            }
        }
        finally
        {
            System.out.println("Final block start");
            try{ connection.close(); } catch(Exception exception) { throw new Exception("Database error"); }
            try{ resultset.close(); } catch(Exception exception) { throw new Exception("Database error"); }
            System.out.println("Final block end");
        }
    }
}

$PROJECT_ROOT/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>testservlet</artifactId>
    <packaging>war</packaging>
    <version>0.1.0-SNAPSHOT</version>
    <name>testservlet</name>
    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <build>
        <finalName>testservlet</finalName>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                        </manifest>
                    </archive>
                    <webResources>
                        <resource>
                            <directory>${project.basedir}/src/main/resources
                            </directory>
                        </resource>
                    </webResources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

The MySQL driver is installed as "mysql-connector-java.jar" (Version 5.1.38) inside the Tomcat lib directory also the database configuration is called by the Spring framework. A HTTP request to localhost:8080/testservlet + a Tomcat restarts results in the following log:

25-Jan-2016 19:11:28.006 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version:        Apache Tomcat/8.0.30
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built:          Dec 1 2015 22:30:46 UTC
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server number:         8.0.30.0
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name:               Windows Server 2012 R2
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version:            6.3
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture:          amd64
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home:             C:\Program Files\Java\jdk1.8.0_66\jre
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version:           1.8.0_66-b18
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor:            Oracle Corporation
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE:         C:\Users\simonwaechter\Downloads\apache-tomcat-8.0.30-windows-x64
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME:         C:\Users\simonwaechter\Downloads\apache-tomcat-8.0.30-windows-x64
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=C:\Users\simonwaechter\Downloads\apache-tomcat-8.0.30-windows-x64\conf\logging.properties
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.endorsed.dirs=C:\Users\simonwaechter\Downloads\apache-tomcat-8.0.30-windows-x64\endorsed
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.base=C:\Users\simonwaechter\Downloads\apache-tomcat-8.0.30-windows-x64
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.home=C:\Users\simonwaechter\Downloads\apache-tomcat-8.0.30-windows-x64
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.io.tmpdir=C:\Users\simonwaechter\Downloads\apache-tomcat-8.0.30-windows-x64\temp
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded APR based Apache Tomcat Native library 1.1.33 using APR version 1.5.1.
25-Jan-2016 19:11:28.037 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
25-Jan-2016 19:11:28.617 INFO [main] org.apache.catalina.core.AprLifecycleListener.initializeSSL OpenSSL successfully initialized (OpenSSL 1.0.1m 19 Mar 2015)
25-Jan-2016 19:11:28.866 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-apr-8080"]
25-Jan-2016 19:11:28.881 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["ajp-apr-8009"]
25-Jan-2016 19:11:28.881 INFO [main] org.apache.catalina.startup.Catalina.load Initialization processed in 1407 ms
25-Jan-2016 19:11:28.929 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service Catalina
25-Jan-2016 19:11:28.929 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet Engine: Apache Tomcat/8.0.30
25-Jan-2016 19:11:29.006 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive C:\Users\simonwaechter\Downloads\apache-tomcat-8.0.30-windows-x64\webapps\testservlet.war
25-Jan-2016 19:11:30.901 INFO [localhost-startStop-1] org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory.getObjectInstance Name = testservlet Ignoring unknown property: value of "JCG Example MySQL database." for "description" property
25-Jan-2016 19:11:30.901 INFO [localhost-startStop-1] org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory.getObjectInstance Name = testservlet Ignoring unknown property: value of "jdbc/testservlet" for "global" property
25-Jan-2016 19:11:31.010 INFO [localhost-startStop-1] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
25-Jan-2016 19:11:31.213 INFO [localhost-startStop-1] org.springframework.web.context.ContextLoader.initWebApplicationContext Root WebApplicationContext: initialization started
25-Jan-2016 19:11:31.213 INFO [localhost-startStop-1] org.springframework.web.context.support.AnnotationConfigWebApplicationContext.prepareRefresh Refreshing Root WebApplicationContext: startup date [Mon Jan 25 19:11:31 CET 2016]; root of context hierarchy
25-Jan-2016 19:11:31.369 INFO [localhost-startStop-1] org.springframework.web.context.support.AnnotationConfigWebApplicationContext.loadBeanDefinitions Found 2 annotated classes in package [com.example.testservlet.configuration]
25-Jan-2016 19:11:32.141 INFO [localhost-startStop-1] org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.registerHandlerMethod Mapped "{[/],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public void com.example.testservlet.controller.HomeController.home() throws java.lang.Exception
25-Jan-2016 19:11:32.187 INFO [localhost-startStop-1] org.springframework.web.servlet.handler.SimpleUrlHandlerMapping.registerHandler Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler]
25-Jan-2016 19:11:32.437 INFO [localhost-startStop-1] org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.initControllerAdviceCache Looking for @ControllerAdvice: Root WebApplicationContext: startup date [Mon Jan 25 19:11:31 CET 2016]; root of context hierarchy
25-Jan-2016 19:11:32.671 INFO [localhost-startStop-1] org.springframework.web.context.ContextLoader.initWebApplicationContext Root WebApplicationContext: initialization completed in 1458 ms
25-Jan-2016 19:11:32.749 INFO [localhost-startStop-1] org.springframework.web.servlet.DispatcherServlet.initServletBean FrameworkServlet 'DispatcherServlet': initialization started
25-Jan-2016 19:11:32.781 INFO [localhost-startStop-1] org.springframework.web.servlet.DispatcherServlet.initServletBean FrameworkServlet 'DispatcherServlet': initialization completed in 32 ms
25-Jan-2016 19:11:32.829 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive C:\Users\simonwaechter\Downloads\apache-tomcat-8.0.30-windows-x64\webapps\testservlet.war has finished in 3,807 ms
25-Jan-2016 19:11:32.829 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-apr-8080"]
25-Jan-2016 19:11:32.843 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-apr-8009"]
25-Jan-2016 19:11:32.843 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 3959 ms
25-Jan-2016 19:11:45.074 INFO [main] org.apache.catalina.core.StandardServer.await A valid shutdown command was received via the shutdown port. Stopping the Server instance.
25-Jan-2016 19:11:45.074 INFO [main] org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler ["http-apr-8080"]
25-Jan-2016 19:11:45.137 INFO [main] org.apache.coyote.AbstractProtocol.pause Pausing ProtocolHandler ["ajp-apr-8009"]
25-Jan-2016 19:11:45.199 INFO [main] org.apache.catalina.core.StandardService.stopInternal Stopping service Catalina
25-Jan-2016 19:11:45.199 INFO [localhost-startStop-2] org.springframework.web.context.support.AnnotationConfigWebApplicationContext.doClose Closing Root WebApplicationContext: startup date [Mon Jan 25 19:11:31 CET 2016]; root of context hierarchy
25-Jan-2016 19:11:45.199 WARNING [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [testservlet] 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. Stack trace of thread:
 java.lang.Object.wait(Native Method)
 java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
 com.mysql.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:43)
25-Jan-2016 19:11:45.215 INFO [main] org.apache.coyote.AbstractProtocol.stop Stopping ProtocolHandler ["http-apr-8080"]
25-Jan-2016 19:11:45.309 INFO [main] org.apache.coyote.AbstractProtocol.stop Stopping ProtocolHandler ["ajp-apr-8009"]
25-Jan-2016 19:11:45.371 INFO [main] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler ["http-apr-8080"]
25-Jan-2016 19:11:45.371 INFO [main] org.apache.coyote.AbstractProtocol.destroy Destroying ProtocolHandler ["ajp-apr-8009"]

I have no idea why this leak happens. There are many answers like deregistering the JDBC driver, but few of them handle the driver in a shared/hosted Tomcat environment where other servlets might access the same JDBC driver.

Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
swaechter
  • 1,357
  • 3
  • 22
  • 46
  • Are you restarting Tomcat or redeploying your application? – Guillaume Polet Jan 25 '16 at 18:35
  • @GuillaumePolet Tomcat is started via the Tomcat Windows batch script "startup.bat" and restarted via "shutdown.bat" + "startup.bat" – swaechter Jan 25 '16 at 18:41
  • 1
    Ok, then you shouldn't really care. The message is only saying that a Thread was started but not stopped (the issue comes from the MySQL Driver itself). Since you're shutting down the JVM, the thread will be killed anyway. If you want, applying the following answer is likely to make the message disappear: http://stackoverflow.com/a/19027873/928711 – Guillaume Polet Jan 25 '16 at 18:45

1 Answers1

1

We ended up with the answer from Guillaume and use the listener.

I still don't know what caused the sporadic crashes, but an update of the Oracle MySQL server solved the problem.

swaechter
  • 1,357
  • 3
  • 22
  • 46