web-dev-qa-db-fra.com

L’application Web semble avoir démarré un thread nommé [Timer-0] mais n’a pas réussi à l’arrêter.

J'utilise Spring Boot 1.5.9.RELEASE + Java 8 + Tomcat 9 + Jersey + Oracle et mon application dispose d'une méthode planifiée définie comme suit:

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

La classe d'emploi:

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

}

De plus, j'ai une classe pour désenregistrer le pilote Oracle comme suit:

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

        }
    }

}

mais en arrêtant Tomcat, j'obtiens l'erreur suivante:

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)

Pourquoi ai-je cette erreur et comment puis-je la réparer?

22
Mahmoud Saleh

Changez votre ScheduleConfig pour utiliser shutdownNow au lieu de shutdown comme méthode de destruction.

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

Je souhaite partager certaines solutions avec l’analyse de la cause première de ce problème.

Pour les utilisateurs Oracle:

Solution # 1:

Vous pouvez supprimer votre pilote Oracle du dossier /WEB-INF/lib Et le placer dans le dossier /lib De Tomcat. Cela peut résoudre votre problème.

Solution # 2:

Vous pouvez utiliser le hack réel en dormant.

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

Lien de ressource:Solution pour "Tomcat ne peut pas arrêter [thread de nettoyage de connexion abandonnée]"

Solution # 3:

Svetlin Zarev n'a rien à craindre. C'est le message standard de Tomcat. Il a donné une analyse des causes fondamentales comme ci-dessous:

Ce problème survient lorsqu'une application a démarré ScheduledExecutor (mais cela se produira avec tout autre Thread/TheadPool) et ne l'a pas arrêté lors de contextDestroyed. Vérifiez donc si vous arrêtez vos threads à l’arrêt d’application/serveur.

Lien de ressource:fuite de mémoire Tomcat8

Solution # 4:

Pour les utilisateurs Oracle, ce message contient plusieurs réponses: Pour éviter une fuite de mémoire, le pilote JDBC a été forcé de ne pas être enregistré


Pour les utilisateurs de MySQL,

Solution # 5:

Analyse des causes profondes avec la solution:

Le thread de nettoyage pour les connexions abandonnées de la classe NonRegisteringDriver a été remanié afin d’avoir une méthode d’arrêt statique. La mémoire a été allouée mais jamais libérée. Si vous rencontrez ce problème de fuite, implémentez l'écouteur de contexte dans votre application avec l'appel AbandonedConnectionCleanupThread.shutdown() dans la méthode contextDestroyed.

Ce problème a été détecté dans des applications exécutées sous le serveur d'applications Tomcat, mais il a peut-être également été appliqué à d'autres serveurs d'applications.

Par exemple:

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

Notez que si le conteneur ne prend pas en charge les annotations, vous ajoutez la description à web.xml:

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

Lien de ressource:https://docs.Oracle.com/cd/E17952_01/connector-j-relnotes-en/news-5-1-23.html

4
SkyWalker

Mes conclusions après avoir effectué quelques tests basés sur votre code et effectué des recherches en ligne:

  • Il n'y a rien à craindre ( lien ). Le processus Tomcat est en cours d'achèvement et il ne reste aucune fuite de mémoire.

  • Même si vous appelez quelque chose comme AbandonedConnectionCleanupThread.shutdown(), vous pouvez toujours obtenir le même avertissement ( link )

  • Cet avertissement se produit lorsque vous appelez startup.sh Et shutdown.sh. Lors de l'exécution de Tomcat à partir d'Eclipse, cet avertissement ne s'affiche pas.

  • Votre méthode d'arrêt pour le Executor est probablement appelée. Pour mes tests, il était appelé même si je ne définissais pas le destroyMethod de l'exécuteur.

  • Dans ce cas, cet avertissement n'est associé à aucun bean Spring Scheduling. Executors.newScheduledThreadPool Retourne un nouveau ScheduledThreadPoolExecutor, qui a la méthode destroy et qui est en train d'être détruit, comme je l'ai déjà indiqué. Vous pouvez déboguer et voir par vous-même.

  • Cependant, il y a quelque part dans votre code appelant new Java.util.Timer, Qui appelle new TimerThread(), ass vu depuis votre journalisation, et comme l'a souligné @ Claudio Corsi.

Pour le déboguer et si vous utilisez Eclipse , vous devez joindre le code source de votre version du JDK. Ouvrez la déclaration de classe (maintenez la touche ctrl enfoncée et choisissez la déclaration ouverte), puis cliquez sur le bouton "Joindre le code source". Assurez-vous de télécharger exactement la même version. Vous n'avez même pas à extraire le zip. Si vous utilisez Maven, attendez un peu qu'il se télécharge tout seul.

Ensuite, placez un point d'arrêt dans le constructeur pour Java.util.Timer Et lancez le débogage de votre application.

Edit : Après avoir identifié une référence à Java.util.Timer, Enregistrez-la (sous forme de bean, si ce n'est pas le cas) et appelez-la cancel méthode sur le contexte détruit.

2
diogenesgg

Il est difficile de dire la cause première mais le nom du fil [Timer-0] donne un indice pour le trouver. Java.util.Timer _ class crée des discussions qui ont un modèle de nom tel que Timer - * comme vous pouvez le voir dans son code source.

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

Peut-être que les bibliothèques qui se trouvent dans votre chemin de classe commencent un thread Timer mais ne l'annulent pas ou le code qui fonctionne dans ce thread est bloqué.

Je peux suggérer de mettre le point d'arrêt dans Java.util.Timer et le déboguer pour trouver les tâches qui y travaillent. Cela peut en indiquer la cause.

1
Mehmet Sunkur

J'ai aussi le même problème avec l'erreur suivante:

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:

Donc, après un moment, j'ai compris que je n'avais pas fait l'installation de maven pour tous les sous-modules de mon application Spring-Boot. Alors, vérifiez si vous rencontrez la même erreur que:

  1. Vous avez couru mvn clean install -U pour tous les sous-modules du projet et pour le projet lui-même.
0
Ashish