Web 应用程序似乎已启动名为 [Timer-0] 的线程,但未能停止该线程对于甲骨文用户对于 MySQL 用户

2022-09-01 02:43:14

我正在使用Spring Boot 1.5.9.RELEASE + Java 8 + Tomcat 9 + Jersey + Oracle,我的应用程序已计划方法定义如下:

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    @Bean(destroyMethod = "shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(100);
    }
}

工作类:

@Component
public class ClearCacheJob {



    @Scheduled(fixedRate = 3600000, initialDelay = 10000)
    public void clearErrorCodesCache() {
        try {
            logger.info("######## ClearCacheJob #########");
        } catch (Exception e) {
            logger.error("Exception in ClearCacheJob", e);
        }
    }

}

另外,我有一个类来取消注册Oracle驱动程序,如下所示:

@WebListener
public class ContainerContextClosedHandler implements ServletContextListener {

    private static final Logger logger = LoggerFactory.getLogger(ContainerContextClosedHandler.class);

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        logger.info("######### contextInitialized #########");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        logger.info("######### contextDestroyed #########");
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                logger.info(String.format("deregistering jdbc driver: %s", driver));
            } catch (SQLException e) {
                logger.info(String.format("Error deregistering driver %s", driver), e);
            }

        }
    }

}

但是当停止Tomcat时,我收到以下错误:

WARNING [Thread-11] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [hai] 
appears to have started a thread named [Timer-0] 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.Object.wait(Unknown Source)
 java.util.TimerThread.mainLoop(Unknown Source)
 java.util.TimerThread.run(Unknown Source)

为什么我会收到此错误,我该如何解决它?


答案 1

我想分享一些解决方案,并分析此问题的根本原因。


对于甲骨文用户

解决方案#1

您应该从Tomcat的文件夹中删除Oracle驱动程序。我遇到了同样的问题,它得到了解决。/lib

注意:让 Oracle 驱动程序位于文件夹中。/WEB-INF/lib

解决方案#2

你可以通过睡眠线程使用真正的黑客。

@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
    logger.info("######### contextDestroyed #########");
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
        Driver driver = drivers.nextElement();
        try {
            DriverManager.deregisterDriver(driver);
            logger.info(String.format("deregistering jdbc driver: %s", driver));
        } catch (SQLException e) {
            logger.info(String.format("Error deregistering driver %s", driver), e);
        }
    }
    try { Thread.sleep(2000L); } catch (Exception e) {} // Use this thread sleep
}

资源链接:“Tomcat 无法停止 [废弃的连接清理线程]”的解决方案

解决方案#3

Svetlin Zarev没有告诉任何值得担心的事情。这是雄猫的标准信息。他给出了根本原因分析如下:

当应用程序已启动 ScheduledExecutor(但任何其他 Thread/TheadPool 都会发生这种情况)并且没有在 contextDestroyed 上将其关闭时,会出现此问题。因此,请检查您是否在应用程序/服务器停止时关闭线程。

资源链接:Tomcat8 内存泄漏

解决方案#4

对于Oracle用户,这篇文章中有多个答案:为了防止内存泄漏,JDBC驱动程序已被强制注销


对于 MySQL 用户

解决方案#5

通过解决方案进行根本原因分析:

非注册驱动程序类中废弃连接的清理线程已重构为具有静态关闭方法。已分配内存,但从未释放。如果遇到此泄漏问题,请使用方法中的调用在应用程序中实现上下文侦听器。AbandonedConnectionCleanupThread.shutdown()contextDestroyed

在 Tomcat 应用程序服务器下运行的应用程序中发现了此问题,但也可能应用于其他应用程序服务器。

例如:

@WebListener
public class YourThreadsListener implements ServletContextListener {
   public void contextDestroyed(ServletContextEvent arg0) {
      try {
          AbandonedConnectionCleanupThread.shutdown();
      } catch (InterruptedException e) {
      }
   }
   ...
}

请注意,如果容器不支持注释,请将说明添加到 web.xml:

<listener>
    <listener-class>user.package.YourThreadsListener</listener-class> 
</listener>

资源链接:https://docs.oracle.com/cd/E17952_01/connector-j-relnotes-en/news-5-1-23.html


答案 2

将您更改为使用而不是作为销毁方法。ScheduleConfigshutdownNowshutdown

@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    @Bean(destroyMethod = "shutdownNow")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(100);
    }
}