4

The Open J2EE Web Template is a showcase application for wicket - JPA with Spring and Hibernate that runs on a Tomcat7 servlet container. Its Maven build script appears to use the components in a standard way.

However, it is affected by an application classloader memory leak when it is undeployed from Tomcat.

Tomcat's "Find Leaks" button confirms the leak. When deployed on Tomcat with VM option -XX:+HeapDumpOnOutOfMemoryError, the generated heap dump can be analysed with Eclipse Memory Analyzer Tool (MAT). MAT identifies the class java.util.logging.Level$KnownLevel as the culprit that prevents garbage collection.

Debugging of the KnownLevel constructor reveals the following stack trace:

java.util.logging.Level$KnownLevel.add(Level.java:477) java.util.logging.Level.(Level.java:212) java.util.logging.Level.(Level.java:190) org.jboss.logging.JDKLevel.(JDKLevel.java:35) org.jboss.logging.JDKLevel.(JDKLevel.java:42) org.jboss.logging.JDKLogger.translate(JDKLogger.java:78) org.jboss.logging.JDKLogger.isEnabled(JDKLogger.java:85) org.jboss.logging.Logger.debugf(Logger.java:563) org.jboss.logging.LoggerProviders.find(LoggerProviders.java:37) org.jboss.logging.LoggerProviders.(LoggerProviders.java:32) org.jboss.logging.Logger.getLogger(Logger.java:2163) org.jboss.logging.Logger.getMessageLogger(Logger.java:2259) org.jboss.logging.Logger.getMessageLogger(Logger.java:2214) org.hibernate.ejb.Ejb3Configuration.(Ejb3Configuration.java:144) org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:74) org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:268) org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:310) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519) org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294) org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225) org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291) org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193) org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:567) org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913) org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464) org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:385) org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:284) org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111) org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4939)

If I understand Classloader leaks: the dreaded "java.lang.OutOfMemoryError: PermGen space" exception correctly, then this classloader leak is to be expected.

What would be the recommended method to avoid this, or what would be an alternative Spring/JPA template web application on Tomcat?

Whome
  • 10,181
  • 6
  • 53
  • 65
user250343
  • 1,163
  • 1
  • 15
  • 24

2 Answers2

3

I have seen this same webapp memory leak on Tomcat7 + OpenJPA2.4.1 + ValidationAPI1.x + hibernate-validator-5.2.1 + jboss-logging.jar

Problem is indeed jboss-logging.jar creates a subclass of java.util.logging.Level instances. May not be a problem if jar is provided by J2EE container but will happen with mywebapp/WEB-INF/lib distribution. I have created a fork of package to disable subclassing. Problems went away and webapp hot redeployments work fine.

Heapdump of GC root path indicates a source of problem, JDKLevel subclass keeps a webapp in memory and soon JVM run out of PermGen memory.

this     - value: org.apache.catalina.loader.WebappClassLoader #2
 <- <classLoader>     - class: org.jboss.logging.JDKLevel, value: org.apache.catalina.loader.WebappClassLoader #2
  <- <class>     - class: org.jboss.logging.JDKLevel, value: org.jboss.logging.JDKLevel class JDKLevel
   <- levelObject     - class: java.util.logging.Level$KnownLevel, value: org.jboss.logging.JDKLevel #6
    <- [1]     - class: java.lang.Object[], value: java.util.logging.Level$KnownLevel #12
     <- elementData     - class: java.util.ArrayList, value: java.lang.Object[] #5160 (10 items)
      <- value     - class: java.util.HashMap$Entry, value: java.util.ArrayList #3532
       <- [0]     - class: java.util.HashMap$Entry[], value: java.util.HashMap$Entry #21639
        <- table     - class: java.util.HashMap, value: java.util.HashMap$Entry[] #280 (16 items)
         <- intToLevels (sticky class)     - class: java.util.logging.Level$KnownLevel, value: java.util.HashMap #375

edit Created JBoss Jira ticket for this bug (https://issues.jboss.org/browse/JBLOGGING-118)

Whome
  • 10,181
  • 6
  • 53
  • 65
  • Your work is highly appreciated. Would you consider filing a trackable issue with the jboss logging project so that your work will eventually be part of the original distribution? I am suggesting this because I am facing additional classloader issues for which I would like to produce bare bones test cases that do not run into THIS problem. – user250343 Oct 05 '15 at 06:50
  • @user250343 Done that already once I found this memory leak (https://github.com/jboss-logging/jboss-logging/pull/21). I don't know if an appropriate developers have seen my request pull. – Whome Oct 05 '15 at 10:44
1

Configure a logging framework other than java.util.logging e.g. Logback.

If no logging is configured at all, or if java.util.logging is configured, jboss logging will create a classloader leak via the JDK bug Level.known can leak memory. See the stack trace in the question where jboss logging creates the custom java.util.logging.Level.

user250343
  • 1,163
  • 1
  • 15
  • 24