tomcat关闭应用时的清理工作(2): 线程的清理

tomcat在关闭时,有时会看到类似下面的警告信息:

2014-7-10 13:44:02 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [] appears to have started a thread named 
[com.taobao.xxx.client.Timer] but has failed to stop it. 
This is very likely to create a memory leak.

这是tomcat关闭应用时检测到了应用启动的线程未被终止,tomcat为防止造成内存泄露,给出上面的警告,并根据配置来决定是否强制停止该线程(默认不会强制停止)。

有时也会有另一种相似的警告信息:

2014-7-10 13:44:02 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application [] is still processing a request that has yet to finish. 
This is very likely to create a memory leak. 
You can control the time allowed for requests to finish 
by using the unloadDelay attribute of the standard Context implementation.

这是tomcat关闭应用时检测到了仍有请求线程未处理完。

上面的2种警告都是在WebappClassLoaderclearReferencesThreads方法里给出的,该方法也是在stop时调用clearReferences方法时调用的:

protected void clearReferences() {
    ...
    // Stop any threads the web application started
    clearReferencesThreads();
    ...
}

clearReferencesThreads方法里,通过找到最顶层的root thread group获取所有的active线程,然后判断这些线程如果是用户线程的话,给出警告:

if (isRequestThread(thread)) {
    log.error(sm.getString("webappClassLoader.warnRequestThread",
                            contextName, thread.getName()));
} else {
    log.error(sm.getString("webappClassLoader.warnThread",
                            contextName, thread.getName()));
}

我们来模拟一下,先看第一种情况:

@WebServlet(name = "MainServlet", urlPatterns = { "/main" }, loadOnStartup = 1)
public class MainServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    public void init() {
        new Thread() {
            public void run() {
              try {
                while (true) {
                    Thread.sleep(1000);
                }
              } catch (Exception e) {
              }
            }
        }.start();
    }
}

在一个servlet初始化时启动了一个线程,没有提供销毁这个线程的机制,当tomcat停止时,会报第一种警告。

再模拟第二种警告情况,在请求时将线程hang住:

public void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    try {
        Thread.sleep(1000 * 200);
    } catch (Exception e) {
    }
}

这时关闭tomcat时会报出第二种警告信息。

默认tomcat并不会强制将这些线程终止,除非设置了clearReferencesStopThreads为true,它判断线程属于某个线程池则延迟一段时间将线程终止,否则直接调用了JDK已不鼓励的Thread.stop方法终止线程。

if (usingExecutor) {
    // Executor may take a short time to stop all the
    // threads. Make a note of threads that should be
    // stopped and check them at the end of the method.
    executorThreadsToStop.add(thread);
} else {
    // This method is deprecated and for good reason. This
    // is very risky code but is the only option at this
    // point. A *very* good reason for apps to do this
    // clean-up themselves.
    thread.stop();
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注