Description
Diego Pettisani opened SPR-16714 and commented
We had a performance issue after upgraded from Spring 3.2.2 to 3.2.16.
Our application is packaged in an EAR file having several "skinny" WARs, so the content inside the EAR is:
.
|-- lib
| -- spring-core-3.2.16.jar
| -- spring-beans-3.2.16.jar
| -- company-platform.jar
| -- etc. etc.
|-- META-INF
| -- application.xml
|-- app1.war
| -- WEB-INF
| -- web.xml
| -- webapp-beans.xml
|-- app2.war
| -- WEB-INF
| -- web.xml
| -- webapp-beans.xml
| -- more wars.....
.
In each web.xml file we have this configuration:
...
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/webapp-beans.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value>ear.context</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
...
The parent context has several hundred of beans.
We developed an internal platform for our applications that often calls the following BeanFactory method:
org.springframework.beans.factory.BeanFactory.getBean(Class)
In turn, The above getBean(Class) method calls the following DefaultListableBeanFactory method:
org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(Class<?>, boolean, boolean)
After upgraded to Spring 3.2.16 the call to the above getBeanNamesForType() method was very slow and CPU consuming. The root cause is the following class loader check:
if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
cache.put(type, resolvedBeanNames);
}
added for fixing a memory leak highlighted in the #16145 and released in Spring 3.2.9.
The check inside the if condition:
ClassUtils.isCacheSafe(type, getBeanClassLoader())
returns always false because:
- type is an object of the EAR class loader
- getBeanClassLoader() returns always the WAR class loader
- the EAR class loader and the WAR class loader are different and they are sibling in the class loader hierarchy
So the following internal caches of the DefaultListableBeanFactory class:
/** Map of singleton and non-singleton bean names, keyed by dependency type */
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);
/** Map of singleton-only bean names, keyed by dependency type */
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>(64);
will always be empty and they will never be used.
Furthermore informations:
- The application server used is Wildfly 10.0.0.Final
- The JDK used is JDK8 update 74.
- I supposed that upgrading our application to the Spring 4.x or 5.x version will not fix the performance regression because the class loader check is still there.
Affects: 3.2.18, 4.3.16, 5.0.5
Attachments:
Issue Links:
- Memory leak when using annotation based auto-wiring in child context [SPR-11520] #16145 Memory leak when using annotation based auto-wiring in child context
Referenced from: commits 46e3a91, 2989f01, 0efa7a0, 295929c
Backported to: 4.3.17