2015-07-03 6 views
0

У меня возникли проблемы Hot Развертывание веб-приложения Spring-MVC 4.0 (не SpringBoot). Я пытаюсь перейти на xml-less и просто использовать JavaConfig. Результат OutOfMemoryErrors, когда я удаляю web.xml или , когда я развертываю пустой web.xml с пустым элементом. Это не происходит каждый раз, когда приложение горячее развертываются, и после успешного горячего развертывания, приложение работает правильно, но после трех или четырех горячих развертываний с этой конфигурацией возникает следующая ошибка:OutOfMemoryError при использовании горячего развертывания приложения SpringMVC для Tomcat7 - возможное отношение к 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) 

Очевидно, что память каким-то образом протекает с такой конфигурацией.

Возможно, может быть, что это веб-приложение использует Log4j2. An earlier Stack Overflow question исследовал это. Если веб-приложение использует только следующие минимальные 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> 

того WebApp может быть горячим развернутым снова и снова без получения этих ошибок.

Может ли кто-нибудь угадать догадки о том, что может происходить здесь?

Обновление: - См. Обсуждение между мной @Makoton и ниже. Похоже, что может возникнуть проблема с сборкой мусора, связанная с загрузкой log4j2 из приложения (способом Java Config) и его загрузкой из web.xml (традиционным способом). См. this article, который развенчивает «классическое» предложение переполнения стека для этой проблемы (аналогично тому, как цитируется Makoton).

Это заставляет меня думать о SpringBoot, который, как я понимаю, загружает Tomcat как часть приложения. Это может быть одним из решений этой проблемы.

ответ

1

Я считаю, что Люк прав, что этот вопрос может быть вызван a bug in Log4j2.

Ошибка в том, что если элемент <display-name> нет в файле web.xml, класс Log4jServletContextListener, отвечающий за управление ресурсами log4j, может запускаться, но не останавливаться на log4j.

Это означает, что очистка не происходит, когда веб-приложение остановлено или перезапущено. JMX MBeans не являются незарегистрированными, но и все потоки не останавливаются, и в результате классы Log4j не выгружаются. Пропущенная память растет каждый раз, когда веб-приложение перезапускается, потому что каждое веб-приложение имеет свой собственный загрузчик классов (поэтому VM рассматривает их как разные классы).

Эта ошибка исправлена ​​в Git master, и исправление будет частью релиза log4j 2.5. Между тем, пожалуйста, используйте элемент <display-name> в вашем web.xml.

+0

Вы хотите сказать, возможно, «Ошибка в том, что если элемент отсутствует в файле web.xml ИЛИ, если нет NO web.xml"? Это была моя ситуация. Я добавил конфигурацию log4j2, потому что это был единственный способ, которым я мог идти. Похоже, вы говорите, что я могу поместить конфигурацию log4j2 обратно в конфигурацию Spring java и иметь эту вещь в web.xml. Или я мог дождаться исправления. У меня все в порядке? –

+0

Если отсутствующий web.xml приводит к тому, что log4j не может найти имя контекста сервлета, тогда да. Я не пробовал с весной. –

+0

Я подтвержу следующие случаи: 1) если отображаемое имя не включено в файл web.xml, но включен параметр контекста log4jConfiguration, утечки памяти не происходят и сообщения об ошибке отсутствуют. 2) если отображаемое имя включено и log4jConfiguration c.p. включая, такой же как прежний. 3) если ни один из параметров не включен в web.xml, происходит утечка памяти. 4) если отображаемое имя включено в web.xml и не содержит конфигурацию log4j в web.xml, но программную конфигурацию log4j, включенную в java, то никакие утечки памяти, исходная отсутствующая ошибка конфигурации 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 отключился, он не отменял регистрацию всех MBeans управления JMX, которые он ранее зарегистрировал. Поскольку эти MBeans остались в внутреннем реестре MBean JVM, классы нераспределенного веб-приложения не могли быть полностью удалены из памяти, потому что были ссылки на экземпляры этих классов.

, как вы в вашем previous question, я тоже видел Log4j2 сформировать сообщение

No Log4j context configuration provided. This is very unusual. 

во время запуска. В моем случае, однако, исправление было несколько странным - в веб-приложении не было <display-name>, установленного в нашем Интернете.xml, и давая ему отображаемое имя, это сообщение исчезло и утечка памяти!

Я подозреваю, что в Log4j2 есть ошибка в том, что он не отменяет регистрацию MBeans, зарегистрированную, если он запущен без контекста, который он упоминает. Передача отображаемого имени является одним из способов создания достаточного контекста, добавление параметра контекста для местоположения конфигурации представляется другим. Я рассмотрю вопрос об этом, если его еще нет. В настоящее время существует 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 библиотеки. Одна из его функций во время остановки веб-приложений - отменить регистрацию любых MBeans, зарегистрированных в веб-приложении, но не отменивших регистрацию. Вы не сможете использовать библиотеку как файл JAR напрямую, потому что ее нужно добавить в web.xml в качестве слушателя, предположительно для тех, кто все еще использует старые версии сервлета. Однако есть некоторые варианты, чтобы обойти это.

    Во-первых, there is only one .java file in this library, и вы могли бы включить его источник (или только части его источника, который отменил регистрацию MBeans) в вашем приложении, если вы также добавите аннотацию к классу. Во-вторых, вы можете расширить класс ClassLoaderLeakPreventor и добавить аннотацию @WebListener к вашему подклассу.

+0

Спасибо, у меня есть запасные циклы и взглянем на ваши предложения. –

+0

Я считаю, что проблема JMX в log4j исправлена, и это '' является отдельной проблемой. –