从 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()
的末尾添加了一条日志记录语句,并验证它正在关闭 DataSources。
我还验证了 MySQL 驱动程序在 Tomcat 的库中,而不是在 WAR 中。
最佳答案
首先,Tomcat 对此无能为力,您正在使用 Java SE 创建一个执行器。因此,应用程序服务器(Java EE)不能也不应该管理您直接从 Java SE 创建的 ExecutorService。如果您想使用 Java EE ExecutorService,请考虑使用 ManagedScheduledExecutorService
,您无需担心关闭,因为它使用应用服务器的线程池。有了这个问题,问题...
您正在使用 shutdownNow()
,这是一种关闭 ExecutorService
的“快速而肮脏”的方式。如果您想轻轻地关闭您的应用程序,我建议您结合使用 ExecutorService.shutdown()
和 ExecutorService.awaitTermination()
。
根据文档,shutdownNow()
不保证实际可以停止什么。
This method does not wait for actively executing tasks to terminate.
...
There are no guarantees beyond best-effort attempts to stop processing actively executing tasks.
如果你关心等待任务停止,你需要使用awaitTermination()
。 shutdown()
或 shutdownNow()
唯一能做的就是调用 interrupt()
,这可能会也可能不会真正停止线程。要等待终止,请执行以下操作:
executor.shutdown(); // or shutdownNow()
if (!executor.isTerminated())
executor.awaitTermination(10, TimeUnit.SECONDS); // wait for up to 10s
关于java - Tomcat + TomcatJDBC ServletContextListener 开启线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34190999/