2011-10-17 126 views
36

重新部署應用程序時,當我在tomcat的重新部署我的應用程序,我得到了以下問題:內存泄漏在Tomcat中

The web application [] created a ThreadLocal with key of type 
[java.lang.ThreadLocal] (value [[email protected]]) 
and a value of type [com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty] 
(value [[email protected]a183d2]) 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 [[email protected]]) and a value of type [java 
    .util.WeakHashMap... 

的的Ehcache似乎製造出弱哈希映射和我得到的消息,這很可能造成內存泄漏。

我在網上搜索,發現這個, http://jira.pentaho.com/browse/PRD-3616但我沒有訪問服務器等。

請讓我知道這些警告是否有任何功能影響或可以忽略?我使用了tomcat管理器中的「查找內存泄漏」選項,它說「沒有發現內存泄漏」

+1

的警告,意味着你的重新部署應用程序而無需重新啓動Tomcat本身的能力是有限的。長期以來,Webapps一直受到這種內存泄露的困擾。除非您重新部署應用程序,否則它們沒有任何影響。我不知道,但是我懷疑Tomcat輸出中的這些消息,這些消息在一兩年後就開始顯示出來,它們將迫使框架構建者在重新啓動後開始自行清理。 –

回答

36

當您重新部署應用程序時,Tomcat會創建一個新的類加載器。舊的類加載器必須被垃圾收集,否則你會得到一個permgen內存泄漏。

Tomcat無法檢查垃圾回收是否有效,但它知道幾個常見的故障點。如果webapp類加載器爲其類由webapp類加載器本身加載的實例設置ThreadLocal,則servlet線程持有對該實例的引用。這意味着類加載器不會被垃圾收集。

Tomcat執行了多次此類檢測,請參閱here for more information。清理線程當地人很困難,您必須在每個訪問線程的ThreadLocal上調用remove()。在實踐中,這隻在重新部署Web應用程序的多次開發過程中非常重要。在生產中,您可能不會重新部署,因此可以忽略。

要真正找出哪些實例定義了線程本地,您必須使用一個分析器。例如JProfiler(免責聲明:我公司開發JProfiler)中的堆棧助手將幫助您找到那些線程本地人。選擇報告的值類(com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty或com.sun.xml.bind.v2.ClassFactory)並顯示累積傳入引用。其中之一將是java.lang.ThreadLocal$ThreadLocalMap$Entry。選擇該傳入引用類型的引用對象並切換到分配視圖。您將看到實例已分配的位置。有了這些信息,你可以決定你是否可以做點什麼。

enter image description here

+2

正如我所看到的,似乎JProfiler不是免費的開源解決方案。你可以建議一些其他的替代方法 – Raghav

+3

嘗試[VisualVM](http://visualvm.java.net/)。 – timomeinen

+0

@Raghav Eclipse內存分析器也非常適合分析堆轉儲。 http://www.eclipse.org/mat/ – Phil

2

我猜你可能看到了這一點,但爲了以防萬一的Ehcache文檔建議把LIB在Tomcat中而不是在WEB-INF/lib目錄下:http://ehcache.org/documentation/integrations/tomcat

+0

更新後的鏈接:http://ehcache.org/generated/2.9.0/html/ehc-all/#page/Ehcache_Documentation_Set%2Fco-tcat_tomcat_issues_and_practices.html%23wwconnect_header然後滾動到'類加載器內存泄漏' –

3

創建線程不正確地清理起來會最終讓你失去記憶 - 在那裏,做到了。

那些誰仍在琢磨快速的解決方案/解決辦法,可以去下面:

  • 如果運行獨立的Tomcat,殺的javaw.exe或工藝軸承它。
  • 如果從eclipse運行,殺死eclipse.exe和java.exe或封閉進程。
  • 仍然沒有解決,檢查任務管理器,很可能導致此問題的進程將以最高內存顯示 用法 - 執行分析並殺死它。

你要善於重新部署的東西,並沒有進行內存問題。

6

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 GitHubPart 6 of his article

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>