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

j'utilise springboot 1.5.9.RELEASE+ Java 8+ tomcat 9 + Jersey+ Oracle et mon application a prévu méthode 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);
        }
    }

}

j'ai aussi une classe pour déréglementer 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)

veuillez indiquer pourquoi je suis arriver cette erreur et comment la corriger, merci.

17
demandé sur BalusC 2018-01-15 00:48:40

4 réponses

Modifier ScheduleConfig 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);
    }
}
4
répondu shazin 2018-01-17 15:54:48

je veux partager des solutions avec l'analyse des causes profondes de ce problème.

Pour Les Utilisateurs D'Oracle:

Solution#1:

Vous pouvez supprimer votre pilote Oracle de /WEB-INF/lib le dossier et le mettre dans Tomcat's /lib dossier. Ça pourrait résoudre votre problème.

Solution#2:

vous pouvez utiliser le vrai hack en dormant Le fil.

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

Ressources Lien:Solution de "Tomcat ne pouvez pas arrêter [Abandonné de nettoyage de la connexion fil]"

Solution#3:

Svetlin Zarev n'a rien dit d'inquiétant. C'est le message standard de tomcat. Il a fait une analyse des causes profondes comme ci-dessous:

Ce problème se produit lorsqu'une application a commencé Planifiéexécuteur (mais cela se produira avec tout autre Thread / TheadPool) et ne l'a pas fermé en bas sur contextDestroyed. Si vérifiez si vous fermez vos threads sur application / server arrêter.

Ressource Lien:Tomcat8 fuite de mémoire

Solution#4:

pour les utilisateurs D'Oracle, il y a de multiples réponses dans ce post: pour prévenir une fuite de mémoire, le pilote JDBC n'a pas été enregistré de force


Pour MySQL les utilisateurs,

Solution#5:

analyse des causes profondes avec Solution:

le fil conducteur du nettoyage des raccordements abandonnés dans les NonRegisteringDriver la classe a été remaniée pour avoir une méthode d'arrêt statique. La mémoire a été attribuée mais jamais libérée. Si vous rencontré ce problème de fuite, mettre en œuvre l'auditeur de contexte dans votre application avec le AbandonedConnectionCleanupThread.shutdown() appel dans le contextDestroyed méthode.

ce problème a été trouvé dans les applications courant sous le Tomcat serveur d'application, mais il peut avoir aussi appliquée à d'autres les serveurs d'application.

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 supporte pas les annotations, vous ajoutez description À web.xml:

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

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

3
répondu SkyWalker 2018-01-20 05:50:10

il est difficile de dire la cause racine mais le nom du thread [Timer-0] donne un indice pour le trouver. java.util.Timer la classe crée des threads qui ont un motif de nom comme Timer-* comme vous pouvez le voir dans son code source.

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

Éventuellement les bibliothèques qui sont dans votre classpath commence par Timer thread mais ne l'annule pas ou le code qui fonctionne dans ce thread est bloqué.

je peut suggérer de mettre un point d'arrêt dans java.util.Timer et le déboguer pour trouver tâches de travail. Il peut indiquer la cause profonde.

1
répondu Mehmet Sunkur 2018-01-17 23:32:42

mes conclusions après avoir effectué quelques tests basés sur votre code et de la recherche en ligne:

  • il n'y a pas de quoi s'inquiéter (lien). Le processus Tomcat est terminé et il n'y a aucune fuite de mémoire.

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

  • Cet avertissement se produit lors de l'appel de startup.sh et shutdown.sh. En exécutant Tomcat à partir D'Eclipse, il ne montre pas cet avertissement.

  • Votre méthode d'arrêt pour l' Executor est probablement appelé. Pour mes tests, il était appelé même si je n'avais pas défini le destroyMethod pour l'exécuteur testamentaire.

  • dans ce cas, la présente mise en garde n'est pas liée à une fève D'ordonnancement de printemps. Executors.newScheduledThreadPool retourne un nouveau ScheduledThreadPoolExecutor, qui a la méthode destroy et il est détruit, comme je l'ai souligné plus tôt. Vous pouvez débogage et de 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 de votre logging, et comme souligné par @Claudio Corsi.

pour le débogage et si vous utilisez Eclipse, vous devez joindre le code source de votre version JDK. Ouvrir la déclaration de classe (tenir ctrl et choisir la déclaration ouverte) et cliquez sur le bouton" Joindre le Code Source". Assurer vous avez téléchargé l' exactement la même version. Tu n'as même pas besoin d'extraire la fermeture éclair. Si vous utilisez Maven, il suffit de tenir sur un peu qu'il va TÉLÉCHARGER pour lui-même.

alors, placez un point de rupture dans le constructeur pour java.util.Timer et commencez à déboguer votre application.

Modifier: après avoir identifié une référence à java.util.Timer, de l'enregistrer (comme un haricot, si c'est pas un) et fait appel à son cancel méthode de destruction de contexte.

1
répondu diogenesgg 2018-01-24 16:12:02