[英]The web application appears to have started a thread named but has failed to stop it. This is very likely to create a memory leak
[英]The web application appears to have started a thread named [Timer-0] but has failed to stop it
我正在使用 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);
}
}
工作類別:
@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)
為什么我會收到此錯誤,我該如何解決?
我想與此問題的根本原因分析分享一些解決方案。
對於 Oracle 用戶
您應該從 Tomcat 的/lib
文件夾中刪除您的 Oracle 驅動程序。 我遇到了同樣的問題,它得到了解決。
注意:讓 Oracle 驅動程序位於/WEB-INF/lib
文件夾中。
您可以通過休眠線程使用真正的 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
}
資源鏈接: “Tomcat 無法停止 [Abandoned connection cleanup thread]”的解決方法
Svetlin Zarev沒有告訴任何人擔心。 這是tomcat的標准消息。 他給出了如下根本原因分析:
當應用程序已啟動 ScheduledExecutor(但這將與任何其他線程/TheadPool 一起發生)並且沒有在 contextDestroyed 上將其關閉時,會發生此問題。 因此,請檢查您是否在應用程序/服務器停止時關閉線程。
資源鏈接: Tomcat8 內存泄漏
對於Oracle用戶,這個帖子有多個答案: 為了防止內存泄漏,JDBC Driver已被強制注銷
對於 MySQL 用戶
解決方案的根本原因分析:
NonRegisteringDriver類中廢棄連接的清理線程被重構為具有靜態關閉方法。 內存已分配但從未釋放。 如果您遇到此泄漏問題,請使用
contextDestroyed
方法中的AbandonedConnectionCleanupThread.shutdown()
調用在您的應用程序中實現上下文偵聽器。在 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
更改您的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);
}
}
根據您的代碼運行一些測試並在線研究后,我的結論是:
沒有什么可擔心的( 鏈接)。 Tomcat 進程正在完成,並且沒有留下任何內存泄漏。
即使您調用AbandonedConnectionCleanupThread.shutdown()
類的方法,您仍然可以獲得相同的警告( 鏈接)
調用startup.sh
和shutdown.sh
時會出現此警告。 從 Eclipse 運行 Tomcat 時,它不會顯示該警告。
您的Executor
的關閉方法可能會被調用。 對於我的測試,即使我沒有為執行程序定義destroyMethod
,它也會被調用。
在這種情況下,此警告與任何 Spring 調度 bean 無關。 Executors.newScheduledThreadPool
返回一個新的ScheduledThreadPoolExecutor
,它具有 destroy 方法並且它正在被銷毀,就像我之前指出的那樣。 您可以自己調試並查看。
但是,在您的代碼中某處調用new java.util.Timer
,它調用new TimerThread()
,從您的日志中可以看出,正如@Claudio Corsi 所指出的那樣。
為了調試它,如果你使用的是 Eclipse ,你必須附上你的 JDK 版本的源代碼。 打開類聲明(按住 ctrl 並選擇打開聲明)並單擊“附加源代碼”按鈕。 確保您已下載完全相同的版本。 您甚至不必解壓縮 zip。 如果您使用的是 Maven,請稍等一下,它會自行下載。
然后,在java.util.Timer
的構造函數中放置一個斷點並開始調試您的應用程序。
編輯:確定對java.util.Timer
的引用后,將其保存(作為 bean,如果它不是一個)並在上下文銷毀時調用其cancel
方法。
很難說根本原因,但線程名稱[Timer-0]提供了找到它的線索。 java.util.Timer
類創建具有類似Timer-*的名稱模式的線程,如您在其源代碼中所見。
public Timer() {
this("Timer-" + serialNumber());
}
您的類路徑中的庫可能會啟動一個Timer線程,但不會取消它,或者在該線程中工作的代碼卡住了。
我可能會建議在java.util.Timer
放置斷點並對其進行調試以查找哪些任務正在處理它。 它可能指出根本原因。
我也遇到了同樣的問題,出現以下錯誤:
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:
所以,過了一會兒,我發現我沒有為我的 spring-boot 應用程序中的所有子模塊進行 maven 安裝。 因此,請仔細檢查您是否遇到相同的錯誤:
mvn clean install -U
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.