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?
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);
}
}
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é
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éthodecontextDestroyed
.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
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.
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.
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:
mvn clean install -U
pour tous les sous-modules du projet et pour le projet lui-même.