2015-07-03 13 views
0

我遇到了麻煩熱部署Spring-MVC 4.0(不是SpringBoot)Web應用程序。我試圖去xml-less,只是使用JavaConfig。 OutOfMemoryErrors導致我刪除web.xml當我部署一個空的web.xml只有一個空的元素。這不會發生每次應用程序是熱部署和成功的熱部署後,應用程序不正常工作,但經過三,四個熱部署這種配置中出現以下錯誤:將SpringMVC應用程序熱部署到Tomcat7時出現OutOfMemoryError - 與log4j2可能存在關聯

Jul 03, 2015 10:49:43 AM org.springframework.web.context.ContextLoader initWebApplicationContext 
    SEVERE: Context initialization failed 
    java.lang.OutOfMemoryError: PermGen space 
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547) 
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) 
      at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303) 
      at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) 
      at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299) 
      at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) 
      at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:220) 
      at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:615) 
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:465) 
      at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403) 
      at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306) 
      at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106) 
      at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5014) 
      at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5524) 
      at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150) 
      at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901) 
      at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877) 
      at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649) 
      at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1081) 
      at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1877) 
      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) 
      at java.util.concurrent.FutureTask.run(FutureTask.java:262) 
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) 
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) 
      at java.lang.Thread.run(Thread.java:745) 

    Jul 03, 2015 10:49:44 AM org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor run 
    SEVERE: Unexpected death of background thread ContainerBackgroundProcessor[StandardEngine[Catalina]] 
    java.lang.OutOfMemoryError: PermGen space 
      at java.util.concurrent.FutureTask.report(FutureTask.java:122) 
      at java.util.concurrent.FutureTask.get(FutureTask.java:188) 
      at org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:816) 
      at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:488) 
      at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1655) 
      at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:328) 
      at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117) 
      at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90) 
      at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1374) 
      at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1546) 
      at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1556) 
      at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1524) 
      at java.lang.Thread.run(Thread.java:745) 

    Exception in thread "ContainerBackgroundProcessor[StandardEngine[Catalina]]" java.lang.OutOfMemoryError: PermGen space 
      at java.util.concurrent.FutureTask.report(FutureTask.java:122) 
      at java.util.concurrent.FutureTask.get(FutureTask.java:188) 
      at org.apache.catalina.startup.HostConfig.deployWARs(HostConfig.java:816) 
      at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:488) 
      at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1655) 
      at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:328) 
      at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117) 
      at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90) 
      at org.apache.catalina.core.ContainerBase.backgroundProcess(ContainerBase.java:1374) 
      at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1546) 
      at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.processChildren(ContainerBase.java:1556) 
      at org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run(ContainerBase.java:1524) 
      at java.lang.Thread.run(Thread.java:745) 

很明顯,內存以這種配置泄漏。

這個Web應用程序使用Log4j2可能相關也可能不相關。 An earlier Stack Overflow question探討了這一點。如果Web應用程序只使用以下最低的web.xml

<?xml version="1.0"?> 
<web-app xmlns="http://java.sun.com/xml/ns/javaee" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
      version="3.0"> 

    <context-param> 
     <param-name>log4jConfiguration</param-name> 
     <param-value>file:///path/to/log4j2.xml</param-value> 
    </context-param> 

</web-app> 

那麼Web應用程序可以熱部署遍地沒有得到這些錯誤。

任何人都可能冒險猜測這裏會發生什麼?

更新: - 請參閱下面的@Makoton和我之間的討論。看起來很可能存在與從應用程序加載log4j2(Java Config方式)或從web.xml加載它(傳統方式)相關的垃圾收集問題。請參閱this article,其中揭示了針對此問題的「經典」堆棧溢出建議(類似於Makoton引用的建議)。

這讓我想起SpringBoot,據我瞭解,它將Tomcat加載爲應用程序的一部分。這可能是解決這個問題的方法之一。

回答

1

我相信盧克是正確的,這個問題可能是由a bug in Log4j2造成的。

錯誤在於如果web.xml中不存在<display-name>元素,負責log4j資源管理的Log4jServletContextListener類只能啓動,但不能停止log4j。

這意味着,當Web應用程序停止或重新啓動沒有清理髮生。 JMX MBean不是未註冊的,但也不停止任何線程,因此Log4j類不會被卸載。每次Web應用程序重新啓動時,泄漏的內存都會增加,因爲每個Web應用程序都有自己的類加載器(所以VM將它們視爲不同的類)。現在

此錯誤被固定在GIT中主站和修正將是log4j的2.5版本的一部分。同時,請在您的web.xml中使用<display-name>元素。

+0

你的意思是說或許「如果元素不存在於web.xml中或者如果沒有web.xml」,那麼錯誤是?那是我的情況。我添加了log4j2配置,因爲這是我可以走的唯一途徑。這聽起來像你說我可以把log4j2配置放回到Spring java配置中,並且只在web.xml中有這個東西。或者我可以等待修復。我有這個好嗎? –

+0

如果缺少web.xml會導致log4j無法找到servlet上下文名稱,那麼是的。我沒有嘗試與春天。 –

+0

我會確認以下情況:1)如果display-name不包含在web.xml中,但包含log4jConfiguration上下文參數,則不會發生內存泄漏,也不會發生關於缺少配置的錯誤消息。 2)如果包含顯示名稱和log4jConfiguration c.p.包括在內,與之前一樣。 3)如果web.xml中沒有包含參數,則會發生內存泄漏。 4)如果web.xml中包含display-name,web.xml中沒有log4j config,但是包含在java中的編程log4j config,則沒有mem泄漏,最初在日誌中看到丟失的配置錯誤msg,但javaconf使它正確。 –

1

很久以前我和tomcat7熱部署有同樣的問題。當我開始使用JAVA_OPTS與

-XX:+ CMSClassUnloadingEnabled

解決我的問題。我想解釋一下,但我認爲這是更好的解釋。

What does JVM flag CMSClassUnloadingEnabled actually do?

http://frankkieviet.blogspot.ca/2006/10/classloader-leaks-dreaded-permgen-space.html

+0

我要給予好評,你爲我推出到閱讀PermGen的一個非常詳實的建議,但因爲它沒有解決我的問題,這不是一個解決方案。但是,它仍然看起來有點不知何故:初始化Log4j2以應用程序方式而不是在容器中以編程方式加載有問題的類,不管它是什麼,並且這根據我目前的假設,會導致內存泄漏,這在內存泄漏時不存在log4j由容器的類加載機制初始化。 –

2

我認爲,這是在Log4j2的錯誤。

我一直在尋找類似的PermGen內存泄漏在我工作的應用程序中。通過使用探查器,我可以看到,當Log4j2關閉時,它並未註銷它之前註冊的所有JMX管理MBean。因爲這些MBean留在了JVM的內部MBean註冊表中,所以未部署的Web應用程序的類無法從內存中完全刪除,因爲有對這些類的實例的引用。

就像你在previous question,我也被看到Log4j2生成啓動過程中消息

No Log4j context configuration provided. This is very unusual. 

。然而在我的情況下,修正有點奇怪 - 網絡應用沒有在我們的網絡中設置<display-name>。XML,並給它一個顯示名稱使這個消息和內存泄漏消失!

我懷疑Log4j2中存在一個錯誤,它不註銷它註冊的MBean,如果它啓動時沒有它提到的上下文。傳遞顯示名稱是創建足夠的上下文的一種方式,爲配置的位置添加上下文參數似乎是另一種方式。 如果還沒有一個,我會考慮提出這個問題。 目前有an issue on the Log4j2 JIRA提到的問題,如果顯示名稱丟失,我現在添加了對這個問題的評論,提到在這種情況下也有內存泄漏。

據我看到它,你有三種選擇。

  1. 住在web.xml文件中。它可能只需要有一個<display-name>
  2. 禁用JMX在Log4j2通過log4j2.disable.jmx設置系統屬性true。 (這最好用命令行參數-Dlog4j2.disable.jmx=true完成。)該選項在Log4j2 JMX documentation中提及。
  3. 查看Mattias Jiderhamn's ClassLoaderLeakPreventor庫。在Web應用程序關閉期間,其功能之一是註銷任何已註冊但未註銷Web應用程序的MBean。您將無法直接將該庫用作JAR文件,因爲它需要作爲偵聽器添加到web.xml中,這大概是爲了那些仍在使用較早的Servlet版本的用戶的好處。不過,有一些選擇可以解決這個問題。

    首先,there is only one .java file in this library,如果您還向該類中添加了@WebListener註釋,則可以在應用程序中包含其源代碼(或者僅包含註銷MBean的源代碼部分)。其次,您可以擴展ClassLoaderLeakPreventor類並將@WebListener註釋添加到您的子類。

+0

謝謝,我有一些備用週期,並會看看你的建議。 –

+0

我相信log4j中的JMX問題已經修復,這個''是一個單獨的問題。 –

相關問題