重新部署应用程序时Tomcat中发生内存泄漏问题

25

我有一个Web应用程序,部署在Tomcat 7.0.70上。我模拟了以下情况:

  1. 我创建了堆转储。
  2. 然后我发送HTTP请求,在服务方法中打印当前线程及其类加载器。然后我调用Thread.currentThread.sleep(10000)。
  3. 与此同时,我点击Tomcat管理页面上的“undeploy this application”。
  4. 我创建了新的堆转储。
  5. 几分钟后,我创建了新的堆转储。


结果


线程转储

在下面的屏幕截图中,您可以看到在我点击“重新部署”之后,所有与此Web应用程序相关的线程都被杀死,除了线程“http-apr-8081-exec-10”。由于我设置了Tomcat的属性“renewThreadsWhenStoppingContext == true”,因此您可以看到一段时间后此线程(“http-apr-8081-exec-10”)被杀死,并且新线程(http-apr-8081-exec-11)代替了它。因此,在创建堆3的转储之后,我不希望有旧的WCL,因为没有任何旧线程或对象。

enter image description here

堆转储1

在下面的两个屏幕截图中,您可以看到当应用程序运行时只有一个WCL(它的参数“started”= true)。 线程“http-apr-8081-exec-10”的contextClassLoader = URLClassLoader(因为它在Tomcat的池中)。 我只谈论这个线程,因为您将能够看到此线程将处理我的未来HTTP请求。

enter image description here

enter image description here

发送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"设置为任何基础类加载器)。

enter image description here

enter image description here

enter image description here

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的引用。

enter image description here

enter image description here

问题。

如何强制告诉Tomcat在线程完成工作后返回旧值contextClassLoader(URLClassLoader)?

如何确保Tomcat不会在线程更新期间复制旧值"contextClassLoader"?

也许,您知道其他解决我的问题的方法吗?

6个回答

11

Tomcat通常不适用于生产环境。我在一些生产应用中使用Tomcat,发现即使堆大小和其他配置正确设置,每次重新加载应用程序时,内存消耗都会不断增加。除非重新启动Tomcat服务,否则内存不会完全释放。我们进行了各种测试试验,如清除日志、重新部署所有应用程序、定期在最不繁忙的时间内每月或每周重新启动Tomcat。但最终我必须说我们已经将生产环境转移到Glassfish和WebSphere。

希望您已经浏览过以下页面:

Java Web应用程序中的内存泄漏

Tomcat修复内存泄漏?

https://developers.redhat.com/blog/2014/08/14/find-fix-memory-leaks-java-application/

http://www.tomcatexpert.com/blog/2010/04/06/tomcats-new-memory-leak-prevention-and-detection

如果您的Web应用程序与Tomcat没有密切耦合,则可以考虑使用其他Web容器。现在我们甚至在开发机器和生产环境中都使用Glassfish,这个决定使我们节省了很多时间。尽管Glassfish和其他此类服务器启动需要更长时间,因为它们不像Tomcat那样轻量级,但生活变得更加容易。


1
我同意你的看法,这是一个已知的Tomcat问题。但我们仍然在生产中使用Tomcat,因为它轻量级且高效。简单地说,我们从不在生产Tomcat上重新部署应用程序,而只是重新启动Tomcat容器。这是有道理的,因为它们是高度使用的应用程序,容器的开销是可以接受的。 - Serge Ballesta
@SergeBallesta - 我在论坛上看到许多人谈论如何在重新部署时找到内存泄漏,但没有人提到 - 只需重新启动Tomcat并忘记重新部署的内存泄漏。人们为什么不愿意在新部署之后重启Tomcat? - veritas
@SergeBallesta - 好的,我在这个链接上找到了答案 - 问题是当您在同一服务器上运行多个应用程序时,您需要重新部署而无需重启https://dev59.com/DnE95IYBdhLWcg3wXcrd - veritas
我不同意关于生产Tomcat问题的看法。Tomcat非常轻量级,停止和创建新的Tomcat实例通常只需要几毫秒。为什么要频繁重新部署Tomcat?为什么一周内要发布多次新版本? - Mladen Adamovic

2
从我的经验来看,防止Tomcat正确GC旧类加载器的原因是我使用的一些框架创建了一些ThreadLocal(并且没有正确处理)。类似于这里所解释的内容:ThreadLocal & Memory Leak
我试图正确地完成这些ThreadLocal,我的泄漏大大减少了。它仍然有泄漏,但是我可以处理比以前多10倍的重新部署。
我肯定会检查您的内存转储到可能与ThreadLocal相关联的对象(它们非常常见,特别是如果您使用某些东西来控制事务或任何线程隔离的内容)。
希望这可以帮助你!

1
Tomcat重新部署中的内存泄漏问题非常古老。唯一真正解决它的方法是重新启动Tomcat而不是重新部署应用程序。如果您有多个应用程序,您需要在不同端口上运行多个Tomcat服务,并使用nginx将它们连接起来。

我们现在已经使用Java EE服务器8年了,并且我们编写了一个程序,在生产环境中每次部署时都会重新启动应用服务器。 - bilelovitch

1
我们有数百个Tomcat实例在多个环境(包括生产环境)运行,我们找到的唯一合理的解决方案是在每天的设定时间(夜间)停止并重新启动每个Tomcat。

我们尝试了许多技巧,但这是我们的可用性要求的持久解决方案。


0

0

在生产环境中,Tomcat通常不是一个好的选择。我曾经在几个生产应用程序中使用Tomcat,并发现即使堆大小和其他配置正确设置,每次重新加载应用程序时,内存消耗都会不断增加。除非重启Tomcat服务,否则内存无法完全回收。我们进行了所有这样的实验,如清除日志、重新部署所有应用程序、定期在最不繁忙的时间内每月或每周重启Tomcat。但最终我必须说,我们已经将生产环境转移到了Glassfish和WebSphere。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接