1

从 Tomcat 取消部署应用程序时,有一些线程处于打开状态。

org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [/services] 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.
org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [/services] appears to have started a thread named [Timer-0] but has failed to stop it. This is very likely to create a memory leak.

该应用程序维护一个 DataSources 的映射并运行 ScheduledExecutorService 以每 5 分钟更新一次映射。

@WebListener
public class DataSourceFactory implements ServletContextListener
{
    private static Map<String, DataSource> rdsDataSourceMap;
    private static ScheduledExecutorService scheduler;
    private static final long CONNECTION_MAP_REFRESH_INTERVAL = 5;

    @Override
    public void contextInitialized(ServletContextEvent event) 
    {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(new Runnable(){
            @Override
            public void run() {
                cacheDatasourceMap();
            }
        }, 0, CONNECTION_MAP_REFRESH_INTERVAL, TimeUnit.MINUTES);
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) 
    {
        scheduler.shutdownNow();
        if (localPool != null) {
            localPool.close();
        }
        for (DataSource ds : rdsDataSourceMap.values()) {
            if (ds != null) {
                ds.close();
            }
        }
    }

    private void cacheDatasourceMap()
    {
        ...
    }

    ....
}

数据源是使用 TomcatJDBC 创建的,具有以下参数:

driver=com.mysql.jdbc.Driver
jmxEnabled=true
testWhileIdle=true
testOnBorrow=true
validationQuery=SELECT 1
testOnReturn=false
validationInterval=30000
timeBetweenEvictionRunsMillis=5000
maxActive=100
maxIdle=20
initialSize=10
maxWait=100000
removeAbandonedTimeout=60
minEvictableIdleTimeMillis=30000
minIdle=10
logAbandoned=true
removeAbandoned=true
jdbcInterceptors=org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer;org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx(threshold=10000)

更新

摆脱后,ScheduledExecutorService我仍然看到 Timer 线程处于打开状态。我在末尾添加了一条日志记录语句,contextDestroyed()并验证它是否已通过关闭数据源。

我还验证了 Tomcat 库中的 MySQL 驱动程序,而不是 WAR 中的 MySQL 驱动程序。

4

1 回答 1

0

首先,Tomcat 对此无能为力,您正在使用 Java SE 创建一个 Executor。因此,应用服务器(Java EE)不能也不应该管理您直接从 Java SE 创建的 ExecutorService。如果您想使用 Java EE ExecutorService,请考虑使用ManagedScheduledExecutorService它,因为它使用应用程序服务器的线程池,因此您无需担心关闭。顺便说一句,进入问题......

您正在使用shutdownNow()which 是一种“快速而肮脏”的方式来关闭ExecutorService. 如果您想温和地降低您的应用程序,我建议您ExecutorService.shutdown()结合使用 with ExecutorService.awaitTermination()

根据文档,shutdownNow()不保证实际可以停止什么。

此方法不等待主动执行的任务终止。
...
除了尽力停止处理正在执行的任务之外,没有任何保证。

如果您关心等待任务停止,则需要使用awaitTermination(). 唯一shutdown()shutdownNow()可以做的是 call interrupt(),它可能会或可能不会真正停止线程。要等待终止,请执行以下操作:

executor.shutdown(); // or shutdownNow()
if (!executor.isTerminated())
    executor.awaitTermination(10, TimeUnit.SECONDS); // wait for up to 10s
于 2015-12-10T00:12:07.320 回答