我有一个Web应用程序,部署在Tomcat 7.0.70上。我模拟了以下情况:
- 我创建了堆转储。
- 然后我发送HTTP请求,在服务方法中打印当前线程及其类加载器。然后我调用Thread.currentThread.sleep(10000)。
- 与此同时,我点击Tomcat管理页面上的“undeploy this application”。
- 我创建了新的堆转储。
- 几分钟后,我创建了新的堆转储。
结果
线程转储
在下面的屏幕截图中,您可以看到在我点击“重新部署”之后,所有与此Web应用程序相关的线程都被杀死,除了线程“http-apr-8081-exec-10”。由于我设置了Tomcat的属性“renewThreadsWhenStoppingContext == true”,因此您可以看到一段时间后此线程(“http-apr-8081-exec-10”)被杀死,并且新线程(http-apr-8081-exec-11)代替了它。因此,在创建堆3的转储之后,我不希望有旧的WCL,因为没有任何旧线程或对象。
堆转储1
在下面的两个屏幕截图中,您可以看到当应用程序运行时只有一个WCL(它的参数“started”= true)。 线程“http-apr-8081-exec-10”的contextClassLoader = URLClassLoader(因为它在Tomcat的池中)。 我只谈论这个线程,因为您将能够看到此线程将处理我的未来HTTP请求。
发送HTTP请求
现在我发送HTTP请求,我的代码中获取有关当前线程的信息。您可以看到我的请求由线程“http-apr-8081-exec-10”处理。
дек 23, 2016 9:28:16 AM c.c.c.f.s.r.ReportGenerationServiceImpl INFO: request has been handled in
thread = http-apr-8081-exec-10, its contextClassLoader = WebappClassLoader
context: /hdi
delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader: java.net.URLClassLoader@4162ca06
然后我点击"重新部署我的Web应用程序",并在控制台中得到以下消息。
дек 23, 2016 9:28:27 AM org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
SEVERE: The web application [/hdi] appears to have started a thread named [http-apr-8081-exec-10] but has failed to stop it. This is very likely to create a memory leak.
Heapd dump 2
以下屏幕截图显示有两个WebAppClassLoader实例。其中一个(编号#1)是旧的(其属性“started”=false)。WCL #2 是在重新部署应用程序后创建的(其属性“started”=true)。 我们审查的线程的contextClassLoader ="org.apache.catalina.loader.WebappClassLoader"。为什么?我预计会看到contextClassLoader = "java.net.URLClassLoader" (毕竟,当任何线程完成工作时,它会返回到Tomcat池中,其属性"contextClassLoader"设置为任何基础类加载器)。
Heapd dump 3
可以看到没有" http-apr-8081-exec-10 "线程,但是存在线程" http-apr-8081-exec-11 "且它的contextClassLoader =" WebappClassLoader"(为什么不是URLClassLoader?)。
最终我们有以下:存在线程“http-apr-8081-exec-11”,它具有对WebappClassLoader #1的引用。当我在WCL#1上进行“Nearest GC Root”时,很明显我将看到对线程11的引用。
问题。
如何强制告诉Tomcat在线程完成工作后返回旧值contextClassLoader(URLClassLoader)?
如何确保Tomcat不会在线程更新期间复制旧值"contextClassLoader"?
也许,您知道其他解决我的问题的方法吗?