a thread named [FileWatchdog] but has failed to stop it.

异常原因:

01-Feb-2018 19:41:28.020 WARNING [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [xxx] appears to have started a thread named [FileWatchdog] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 java.lang.Thread.sleep(Native Method)
 org.apache.log4j.helpers.FileWatchdog.run(FileWatchdog.java:104)
问题起因:

1.在web.xml里面配置了

	<context-param>
        <param-name>contextLog4jRefreshInterval</param-name>
        <param-value>60000</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
	</listener>
这样 org.springframework.web.util.Log4jConfigListener会在初始化log的时候进行初始化 org.apache.log4j.helpers.FileWatchdog的子类( org.apache.log4j.PropertyWatchdog[log4j配置源为.properties时]或者org.apache.log4j.xml.XMLWatchdog【log4j 配置源为.xml时】 优先为.xml)
/** Parameter specifying the refresh interval for checking the log4j config file */
public static final String REFRESH_INTERVAL_PARAM = "log4jRefreshInterval";
省略中......

 // Check whether refresh interval was specified.
String intervalString = servletContext.getInitParameter(REFRESH_INTERVAL_PARAM);
if (StringUtils.hasText(intervalString)) {
	// Initialize with refresh interval, i.e. with log4j's watchdog thread,
	// checking the file in the background.
	try {
		long refreshInterval = Long.parseLong(intervalString);
		Log4jConfigurer.initLogging(location, refreshInterval);
	}
	catch (NumberFormatException ex) {
		throw new IllegalArgumentException("Invalid 'log4jRefreshInterval' parameter: " + ex.getMessage());
	}
}
在Log4jConfigurer.initLogging(location, refreshInterval);这个方法里面初始化监控狗:

	public static void initLogging(String location, long refreshInterval) throws FileNotFoundException {
		String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);
		File file = ResourceUtils.getFile(resolvedLocation);
		if (!file.exists()) {
			throw new FileNotFoundException("Log4j config file [" + resolvedLocation + "] not found");
		}

		if (resolvedLocation.toLowerCase().endsWith(XML_FILE_EXTENSION)) {
			DOMConfigurator.configureAndWatch(file.getAbsolutePath(), refreshInterval);
		}
		else {
			PropertyConfigurator.configureAndWatch(file.getAbsolutePath(), refreshInterval);
		}
	}
  static
  public
  void configureAndWatch(String configFilename, long delay) {
    XMLWatchdog xdog = new XMLWatchdog(configFilename);
    xdog.setDelay(delay);
    xdog.start();
  }

或者:
  static
  public
  void configureAndWatch(String configFilename, long delay) {
    PropertyWatchdog pdog = new PropertyWatchdog(configFilename);
    pdog.setDelay(delay);
    pdog.start();
  }

造成这个问题的描述参考:http://www.bubuko.com/infodetail-1812902.html 

说一下解决方案吧:

1.自定义FileWatchdog的子类  编写一个stopFileWatchdog方法,在stop方法里面引用 钩子 interrupted 设为true 

这样就可以结束FileWatchdog自定义子类的run方法了

子类代码:

    private static ContextPropertyWatchdog contextPropertyWatchdog;
    private volatile boolean interrupted = false;

    private ContextPropertyWatchdog(String filename) {
        super(filename);
    }

    public static synchronized ContextPropertyWatchdog getContextPropertyWatchdog(String filename){
        if (null == contextPropertyWatchdog){
            contextPropertyWatchdog = new ContextPropertyWatchdog(filename);
        }
        return contextPropertyWatchdog;
    }

    public void doOnChange() {
        new PropertyConfigurator().doConfigure(filename,
                LogManager.getLoggerRepository());
    }

    public void stopWatchdog(){
        this.interrupted = true;
    }

    public void run() {
        while(!this.interrupted) {
            try {
                Thread.sleep(delay);
            } catch(InterruptedException e) {
                // no interruption expected
            }
            checkAndConfigure();
        }
    }
}

调用:

监听器初始化方法中:  contextInitialized

    public void contextInitialized(ServletContextEvent servletContext) {
        String location = servletContext.getServletContext().getInitParameter("log4jConfigLocation");
        String intervalString = servletContext.getServletContext().getInitParameter("contextLog4jRefreshInterval");
        if (StringUtils.hasText(location) && StringUtils.hasText(intervalString)) {
            try {
                long refreshInterval = Long.parseLong(intervalString);
                String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);
                File file = ResourceUtils.getFile(resolvedLocation);
                if (!file.exists()) {
                    throw new FileNotFoundException("Log4j config file [" + resolvedLocation + "] not found");
                } else {
                    stringName = file.getAbsolutePath();
                    ContextPropertyWatchdog pDog = ContextPropertyWatchdog.getContextPropertyWatchdog(stringName);
                    pDog.setDelay(refreshInterval);
                    pDog.start();
                }

            } catch (NumberFormatException var5) {
                throw new IllegalArgumentException("Invalid 'log4jRefreshInterval' parameter: " + var5.getMessage());
            } catch (FileNotFoundException var6) {
                throw new IllegalArgumentException("Invalid 'log4jConfigLocation' parameter: " + var6.getMessage());
            }
        }
    }
销毁方法中:contextDestroyed
if ( !"".equals(stringName) ){
                    ContextPropertyWatchdog contextPropertyWatchdog = ContextPropertyWatchdog.getContextPropertyWatchdog(stringName);
                    contextPropertyWatchdog.stopWatchdog();
                    contextPropertyWatchdog.join();
                    logger.info("=========================log4j 配置文件监控 退出====================");
                }
其中stringName是自己定义的一个常量:

private String stringName = "";

这个就可以完成这个功能了,但是这样有个缺点就是在tomcat - shutdown的时候会先等待监控狗sleep醒来之后在停止tomcat,时间会稍微多一点。

对了web.xml文件也进行了修改为:

	<context-param>
        <param-name>contextLog4jRefreshInterval</param-name>
        <param-value>60000</param-value>
	</context-param>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当一个web应用程序启动了一个线程,但没有成功停止它时,就很可能会造成内存泄漏。这个线程的堆栈跟踪提示了一个java.lang.Object.wait(Native Method)方法。 有人在一个类似的问题上给出了解答,说明这个问题是由Axis框架引起的,而不是应用程序本身的问题。然而,上下文启动失败和堆外内存不足可能是由于PermGen空间快速耗尽。猜测可能是由于线程本地内存泄漏更为严重?建议使用 -XX:HeapDumpOnOutOfMemoryError参数来诊断这个问题。 进一步查阅相关文件后,发现可能是使用的框架忘记清理缓存导致的。例如,在logback中如果出现这个问题,Tomcat会报告此错误,这是因为线程局部变量是根据HTTP工作线程创建的。即使应用程序还没有部署,HTTP线程仍然存在,而这些线程局部变量也存在。这可能导致内存泄漏。无法卸载[com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#2],并且在将来重用这些线程时可能会出现问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [The web application [ssms] appears to have started a thread named but has failed to stop it. This is](https://blog.csdn.net/ancientear/article/details/87860932)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值