Web应用程序似乎已启动名为[Timer-0]的线程但未能将其停止

时间:2018-01-14 21:48:40

标签: java multithreading tomcat spring-boot

我正在使用 springboot 1.5.9.RELEASE + Java 8 + tomcat 9 + 泽西 + Oracle ,我的应用程序的计划方法定义如下:

.vtt

工作班:

@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);
    }
}

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

@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);
        }
    }

}

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

@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);
            }

        }
    }

}

请告知我为什么会收到此错误以及如何解决此问题,谢谢。

5 个答案:

答案 0 :(得分:5)

将您的 bacardi breezer aldi rum white coconut bacardi 0 2 0 1 0 0 breezer 2 0 0 0 0 0 aldi 0 0 0 1 1 0 rum 1 0 1 0 1 1 white 0 0 1 1 0 0 coconut 0 0 0 1 0 0 drinks 0 0 0 1 0 1 daniel 0 0 0 1 0 0 改为使用ScheduleConfig而不是shutdownNow作为销毁方法。

shutdown

答案 1 :(得分:4)

我想与这个问题的根本原因分析分享一些解决方案。

  

对于Oracle用户:

<强>溶液#1:

您可以从/WEB-INF/lib文件夹中删除Oracle驱动程序,并将其放在Tomcat的/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
}

资源链接: Solution to “Tomcat can’t stop [Abandoned connection cleanup thread]”

<强>解决方案#3:

Svetlin Zarev没有任何担心。这是tomcat的标准消息。他已经给出了如下的根本原因分析:

  

应用程序启动时发生此问题   ScheduledExecutor(但这将与任何其他人一起发生   Thread / TheadPool)并没有在contextDestroyed上关闭它。所以   检查您是否在应用程序/服务器上关闭线程   停止。

资源链接: Tomcat8 memory leak

<强>溶液#4:

对于Oracle用户,此帖子中有多个答案:To prevent a memory leak, the JDBC Driver has been forcibly unregistered

对于MySQL用户,

<强>解决方案#5:

解决方案的根本原因分析:

  

中放弃连接的清理线程    NonRegisteringDriver 类被重构为具有静态关闭方法。内存已分配,但从未发布。如果你   遇到这个泄漏问题,在你的实现上下文监听器   应用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 :(得分:2)

根据您的代码运行一些测试后的结论 和在线研究:

  • 没有什么可担心的(link)。 Tomcat进程正在完成,并且没有留下内存泄漏。

  • 即使您拨打AbandonedConnectionCleanupThread.shutdown()之类的内容, 你仍然可以得到相同的警告(link

  • 调用startup.shshutdown.sh时会出现此警告。 从Eclipse运行Tomcat时,它没有显示Warning。

  • 可能会调用Executor的关闭方法。 对于我的测试,即使我没有定义destroyMethod,它也会被调用 执行人。

  • 在这种情况下,此警告与任何Spring Scheduling bean无关。 Executors.newScheduledThreadPool返回新的ScheduledThreadPoolExecutor, 它有破坏方法,它正在被破坏,就像我之前指出的那样。您可以自己调试和查看。

  • 但是,您的代码呼叫new java.util.Timer的某个地方, 调用new TimerThread(), 从你的伐木中看到的,正如@Claudio Corsi所指出的那样。

为了调试它并且如果您使用的是Eclipse , 你必须附上JDK版本的源代码。 打开类声明(按住ctrl并选择打开声明) 并单击&#34;附加源代码&#34;按钮。确保你已经下载了 完全相同的版本。你甚至不必提取拉链。 如果你正在使用Maven,只需稍等一下即可自行下载。

然后,在java.util.Timer的构造函数中放置一个断点 并开始调试您的应用程序。

修改:在确定对java.util.Timer的引用后,将其保存(如果它不是一个bean),并在上下文中调用其cancel方法破坏。

答案 3 :(得分:1)

很难说根本原因但是线程名称​​ [Timer-0] 提供了找到它的线索。 java.util.Timer类创建的线程具有名称模式,如 Timer - * ,如您在其源代码中所见。

public Timer() {
    this("Timer-" + serialNumber());
}

您的类路径中的库可能会启动 Timer 线程,但不会取消它或者此线程中的代码卡住了。

我可能会建议在java.util.Timer中放置断点并调试它以查找正在处理它的任务。它可能指出了根本原因。

答案 4 :(得分:0)

我也遇到相同的问题,但出现以下错误:

The web application [ROOT] appears to have started a thread named [cluster-ClusterId{value='5d29b78967e4ce07d9bb8705', description='null'}-localhost:27017] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:

因此,过了一会儿,我发现我没有对spring-boot应用程序中的所有子模块进行maven安装。因此,请仔细检查您是否遇到以下错误:

  1. 您已经为项目中的所有子模块以及项目本身运行了mvn clean install -U