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

时间:2011-10-17 00:17:15

标签: java memory-leaks ehcache

当我在tomcat中重新部署我的应用程序时,我遇到以下问题:

 The web application [] created a ThreadLocal with key of type
 [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@10d16b])
 and a value of type [com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty]
(value [com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty@1a183d2]) but 
 failed to remove it when the web application was stopped. 
 This is very likely to create a memory leak.

另外,我在我的应用程序中使用ehcache。这似乎也导致以下异常。

     SEVERE: The web application [] created a ThreadLocal with key of type [null] 
     (value [com.sun.xml.bind.v2.ClassFactory$1@24cdc7]) and a value of type [java
     .util.WeakHashMap... 

ehcache似乎创建了一个弱哈希映射,我得到的消息是这很可能会造成内存泄漏。

我在网上搜索并发现了这个,   http://jira.pentaho.com/browse/PRD-3616但我无法访问服务器。

如果这些警告有任何功能影响或者可以忽略,请告诉我们?我在tomcat管理器中使用了“Find Memory leaks”选项,它说“没有发现内存泄漏”

4 个答案:

答案 0 :(得分:39)

重新部署应用程序时,Tomcat会创建一个新的类加载器。必须对旧的类加载器进行垃圾回收,否则会导致permgen内存泄漏。

Tomcat无法检查垃圾收集是否有效,但它知道几个常见的失败点。如果webapp类加载器设置一个ThreadLocal,其实例的类由webapp类加载器本身加载,则servlet线程将保存对该实例的引用。这意味着类加载器不会被垃圾回收。

Tomcat进行了大量此类检测,请参阅here for more information。清理线程本地很困难,您必须在每个访问过的线程中的remove()上调用ThreadLocal。实际上,在您多次重新部署Web应用程序时,这在开发过程中非常重要。在生产中,您可能不会重新部署,因此可以忽略它。

要真正找出定义线程本地的实例,必须使用分析器。例如JProfiler中的堆walker(免责声明:我的公司开发JProfiler)将帮助您找到这些线程本地。选择报告的值类(com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty或com.sun.xml.bind.v2.ClassFactory)并显示累积的传入引用。其中一个是java.lang.ThreadLocal$ThreadLocalMap$Entry。选择该传入引用类型的引用对象,然后切换到分配视图。您将看到实例已分配的位置。有了这些信息,您就可以决定是否可以对此做些什么。

enter image description here

答案 1 :(得分:7)

Mattias Jiderhamn非常出色 6-part article非常清楚地解释了关于类加载器泄漏的理论和实践。更好的是,他还发布了一个jar文件,我们可以将其包含在我们的war文件中。我在我的网络应用程序上尝试过,jar文件就像一个魅力! jar文件名为classloader-leak-prevention.jar。要使用它就像将它添加到我们的web.xml

一样简单
<listener>
  <listener-class>se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor</listener-class>
</listener>

然后将其添加到我们的pom.xml

<dependency>
  <groupId>se.jiderhamn</groupId>
  <artifactId>classloader-leak-prevention</artifactId>
  <version>1.15.2</version>
</dependency>

有关详细信息,请参阅 project home page hosted on GitHub 要么 Part 6 of his article

答案 2 :(得分:2)

我猜你可能已经看过了这个,但以防ehcache doc推荐给put the lib in tomcat and not in WEB-INF/lib

答案 3 :(得分:1)

我建议在thread locals中初始化ServletRequestListener

ServletRequestListener有两种方法:一种用于初始化,一种用于销毁。

这样,您就可以清理ThreadLocal。例如:

public class ContextInitiator implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        context = new ThreadLocal<ContextThreadLocal>() {
            @Override
            protected ContextThreadLocal initialValue() {
                ContextThreadLocal context = new ContextThreadLocal();
                return context;
            }
        };
        context.get().setRequest(sre.getServletRequest());
    }
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        context.remove();
    }
}

web.xml

<listener>
    <listener-class>ContextInitiator</listener-class>
</listener>