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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48254517/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 02:48:19  来源:igfitidea点击:

The web application appears to have started a thread named [Timer-0] but has failed to stop it

javamultithreadingtomcatspring-boot

提问by Mahmoud Saleh

I am using Spring Boot 1.5.9.RELEASE + Java 8 + Tomcat 9 + Jersey + Oracle and my app has scheduled method defined as follows:

我正在使用 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);
    }
}

The job class:

工作类别:

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

}

Also I have a class to deregister the Oracle driver as follows:

我还有一个类来取消注册 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);
            }

        }
    }

}

but when stopping Tomcat I am getting the following error:

但是当停止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)

Why am I getting this error and how can I fix it?

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

回答by shazin

Change your ScheduleConfigto use shutdownNowinstead of shutdownas destroy method.

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

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

回答by Mehmet Sunkur

It is hard to say root cause but thread name [Timer-0]gives a clue to find it. java.util.Timerclass creates threads which has name pattern like Timer-*as you can see in it's source code.

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

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

Possibly the libraries that are in your classpath starts a Timerthread but doesn't cancel it or the code which is working in this thread stuck.

可能是您的类路径中的库启动了一个Timer线程,但没有取消它,或者在该线程中工作的代码卡住了。

I may suggest put breakpoint in java.util.Timerand debug it to find which tasks is working on it. It may point the root cause.

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

回答by SkyWalker

I want to share some solutions with root cause analysis of this issue.

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

For Oracle Users:

对于 Oracle 用户:

Solution#1:

解决方案#1:

You should remove your Oracle driver from Tomcat's /libfolder. I was facing the same issue and it got resolved.

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

Note:Let the oracle driver be in /WEB-INF/libfolder.

注意:让 oracle 驱动程序在/WEB-INF/lib文件夹中。

Solution#2:

解决方案#2:

You can use real hack by sleeping thread.

您可以通过休眠线程使用真正的 hack。

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

Resource Link:Solution to “Tomcat can't stop [Abandoned connection cleanup thread]”

资源链接:“Tomcat无法停止[Abandoned connection cleanup thread]”的解决方法

Solution#3:

解决方案#3:

Svetlin Zarevhas told nothing to worry about. It is the standard message of tomcat. He has given root cause analysis like below:

Svetlin Zarev说没有什么可担心的。这是tomcat的标准消息。他给出了如下根本原因分析:

This problem is occurred when an application has started ScheduledExecutor (but this will happen with any other Thread/TheadPool) and didn't shut it down on contextDestroyed. So check if you are shutting down your threads on application/server stop.

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

Resource Link:Tomcat8 memory leak

资源链接:Tomcat8内存泄漏

Solution#4:

解决方案#4:

For Oracle users, there are multiple answers in this post: To prevent a memory leak, the JDBC Driver has been forcibly unregistered

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



For MySQL users,

对于 MySQL 用户,

Solution#5:

解决方案#5:

Root Cause Analysis with Solution:

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

The cleanup thread for abandoned connections in the NonRegisteringDriverclass was refactored to have a static shutdown method. Memory was allocated but never released. If you encountered this leak problem, implement the context listener in your application with the AbandonedConnectionCleanupThread.shutdown()call in the contextDestroyedmethod.

This issue was found in applications running under the Tomcat application server, but it might have also applied to other application servers.

For example:

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

Note that if container does not support annotations, you add the description to web.xml:

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

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>

Resource Link:https://docs.oracle.com/cd/E17952_01/connector-j-relnotes-en/news-5-1-23.html

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

回答by diogenesgg

My conclusions after running a few tests based on your code and researching online:

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

  • There's nothing to worry about (link). Tomcat process is being finished and there's no memory leaks left behind.

  • Even if you call something like AbandonedConnectionCleanupThread.shutdown(), you could still get that same Warning (link)

  • This warning happens when calling startup.shand shutdown.sh. When running Tomcat from Eclipse, it doesn't show that Warning.

  • Your shutdown method for the Executoris likely being called. For my tests, it was getting called even if I didn't define the destroyMethodfor the executor.

  • In this case, this warning is not related to any Spring Scheduling bean. Executors.newScheduledThreadPoolreturns a new ScheduledThreadPoolExecutor, which has the destroy method and it is getting destroyed, like I pointed out earlier. You can debug and see it for yourself.

  • However, there's somewhere at your code calling new java.util.Timer, which calls new TimerThread(), ass seen from your logging, and as pointed out by @Claudio Corsi.

  • 没有什么可担心的(链接)。Tomcat 进程正在完成,没有留下任何内存泄漏。

  • 即使你调用类似的东西AbandonedConnectionCleanupThread.shutdown(),你仍然可以得到同样的警告(链接

  • 调用startup.sh和时会发生此警告shutdown.sh。从 Eclipse 运行 Tomcat 时,它不会显示该警告。

  • 您的关闭方法Executor可能正在被调用。对于我的测试,即使我没有destroyMethod为 executor定义它也会被调用。

  • 在这种情况下,此警告与任何 Spring 调度 bean 无关。 Executors.newScheduledThreadPool返回一个 new ScheduledThreadPoolExecutor,它具有 destroy 方法并且它正在被销毁,就像我之前指出的那样。你可以自己调试看看。

  • 但是,正如@Claudio Corsi 所指出的那样,您的代码调用中存在某处new java.util.Timer调用new TimerThread(), ass 从您的日志中看到的内容。

In order to debug it and if you are using Eclipse, you have to attache the source code for your JDK version. Open the class declaration (hold ctrl and choose open declaration) and click the "Attach Source Code" button. Make sure you have dowloaded the exact same version. You don't even have to extract the zip. If you're using Maven, just hold on a bit that it will download for itself.

为了调试它,如果您使用的是 Eclipse,则必须附上 JDK 版本的源代码。打开类声明(按住 ctrl 并选择打开声明)并单击“附加源代码”按钮。确保您下载了完全相同的版本。您甚至不必解压缩 zip。如果您使用的是 Maven,请稍等一下,它会自行下载。

Then, place a breakpoint in the constructor for java.util.Timerand start debugging your application.

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

Edit: After identifying a reference to java.util.Timer, save it (as a bean, if it's not one) and call its cancelmethod on context destroy.

编辑:识别对 的引用后java.util.Timer,将其保存(作为一个 bean,如果它不是一个 bean)并cancel在上下文销毁时调用它的方法。

回答by Ashish

I have also got the same issue with following error:

我也遇到了以下错误的相同问题:

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:

So, after a while I figured it out that I didn't do the maven install for all sub modules in my spring-boot application. So, double check if you are having same error that:

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

  1. You have run mvn clean install -Ufor all sub modules in the project and for the project itself too.
  1. 您已经mvn clean install -U为项目中的所有子模块以及项目本身运行了。