Кажется, что веб-приложение запустило поток с именем [Timer-0], но не остановило его
Я использую Spring Boot 1.5.9.RELEASE + Java 8 + Tomcat 9
+ Джерси + 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);
}
}
Класс работы:
@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);
}
}
}
Также у меня есть класс для отмены регистрации драйвера 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);
}
}
}
}
но при остановке 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)
Почему я получаю эту ошибку и как ее исправить?
Ответы
Ответ 1
Измените ScheduleConfig
, чтобы использовать shutdownNow
вместо shutdown
как метод destroy.
@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);
}
}
Ответ 2
Я хочу поделиться некоторыми решениями с анализом первопричин этой проблемы.
For Oracle Users:
Решение # 1:
Вам следует удалить драйвер Oracle из папки Tomcat /lib
.
Я столкнулся с той же проблемой, и она была решена.
Примечание. Пусть драйвер оракула находится в папке /WEB-INF/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
}
Ссылка на ресурс: Решение'Tomcat не может остановиться [Поток очистки заброшенного соединения]'
Решение # 3:
Светлин Зарев сказал, что беспокоиться не о чем. Это стандартное сообщение tomcat. Он дал анализ основных причин, как показано ниже:
Эта проблема возникает при запуске приложения ScheduledExecutor (но это произойдет с любым другим Thread/TheadPool) и не закрывали его на contextDestroyed. Так проверьте, закрываете ли вы свои потоки на сервере приложений/ остановить.
Ссылка на ресурс: утечкапамяти в Tomcat8
Решение # 4:
Для пользователей Oracle в этом посте есть несколько ответов: Чтобы предотвратить утечку памяти, драйвер JDBC был принудительно незарегистрирован
Для пользователей 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
Ответ 3
Мои выводы после запуска нескольких тестов на основе вашего кода
и исследование онлайн:
-
Не о чем беспокоиться (ссылка).
Процесс Tomcat завершается, и никаких утечек памяти не осталось.
-
Даже если вы вызываете что-то вроде AbandonedConnectionCleanupThread.shutdown()
,
вы все равно можете получить такое же предупреждение (ссылка)
-
Это предупреждение появляется при вызове startup.sh
и shutdown.sh
.
При запуске Tomcat из Eclipse он не показывает это предупреждение.
-
Вероятно вызывается метод выключения для Executor
.
Для моих тестов он вызывался, даже если я не определил destroyMethod
для исполнителя.
-
В этом случае это предупреждение не связано ни с какими Spring Scheduling bean.
Executors.newScheduledThreadPool
возвращает новый ScheduledThreadPoolExecutor
,
который имеет метод уничтожения, и он разрушается, как я уже указывал ранее. Вы можете отлаживать и просматривать его сами.
-
Однако, где-то в вашем коде, вызывающем new java.util.Timer
,
который вызывает new TimerThread()
,
задница, видимая из вашего ведения журнала, и, как указано @Claudio Corsi.
Чтобы отладить его, и если вы используете Eclipse,
вам необходимо прикрепить исходный код для вашей версии JDK.
Откройте объявление класса (удерживайте ctrl и выберите открытое объявление)
и нажмите кнопку "Прикрепить исходный код". Убедитесь, что вы загрузили
точно такой же версия. Вам даже не нужно извлекать почтовый индекс.
Если вы используете Maven, просто держитесь немного за то, что он загрузится для себя.
Затем поместите точку останова в конструкторе для java.util.Timer
и начать отладку вашего приложения.
Изменить. После определения ссылки на java.util.Timer
сохраните ее (как bean, если она не одна) и вызовите ее метод cancel
в контексте destroy.
Ответ 4
Трудно сказать первопричину, но имя потока [Timer-0] дает ключ к его поиску. java.util.Timer
класс создает потоки, у которых есть шаблон имен, например Timer- *, как вы можете видеть в нем исходный код.
public Timer() {
this("Timer-" + serialNumber());
}
Возможно, библиотеки, которые находятся в вашем пути к классам, запускают поток Timer, но не отменяют его, или код, который работает в этом потоке, застрял.
Я могу предложить поставить точку останова в java.util.Timer
и отладить ее, чтобы найти, какие задачи работают над ней. Это может указывать на основную причину.
Ответ 5
Я также получил ту же проблему со следующей ошибкой:
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:
Итак, через некоторое время я понял, что не установил maven для всех подмодулей в моем приложении с весенней загрузкой. Итак, дважды проверьте, если у вас есть та же ошибка, что:
- Вы запустили
mvn clean install -U
для всех mvn clean install -U
в проекте и для самого проекта.