-
Notifications
You must be signed in to change notification settings - Fork 38.9k
Description
janardhanan vembunarayanan opened SPR-5139 and commented
In my application I am using Spring as a container to store objects.
During the life cycle of my application the values of the these objects can change and I am using the destroyBean() and removeBeanDefinition() of ConfigurableListableBeanFactory and recreating the same using registerBeanDefinition(..).
If I do this process in a for loop continuously I am getting OutOfMemoryError. The reason is in the class DefaultSingletonBeanRegistry the following data structures are growing continuously. Is this a known bug in Spring API destroyBean() and removeBeanDefinition()?
I have attached the test cases and the spring xml file I used for causing this issue. They are in the same file SpringBug.txt.
This issue is coming when I have a Singleton and it has an inner bean.
/** Map between dependent bean names: bean name --> Set of dependent bean names */
private final Map dependentBeanMap = CollectionFactory.createConcurrentMapIfPossible(16 );
/** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
private final Map dependenciesForBeanMap = CollectionFactory.createConcurrentMapIfPossible(16 );
Test Case for the crash is given below along with the Spring configuration.
<code>
package com.debug.spring.modified;
import org.junit.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringBugModified {
private long objectid = 1;
@Test
public void testSpringBug() throws Exception {
ClassPathXmlApplicationContext configuration = new ClassPathXmlApplicationContext(
new String[] { "classpath:com/debug/spring/modified/testspringbug.xml" });
System.out.println("*** Singleton ***");
TestSingletonBeanBug tBeanSingleton = (TestSingletonBeanBug) configuration.getBean("TestBeanSingletonBug");
count(configuration, tBeanSingleton.getInnerBean());
for (;;) {
configuration.getBeanFactory().destroyBean("TestBeanSingletonBug",tBeanSingleton);
((DefaultListableBeanFactory) configuration.getBeanFactory()).removeBeanDefinition("TestBeanSingletonBug");
BeanDefinition beanDefinitionSingleton = createTestSingletonBeanBug();
((DefaultListableBeanFactory) configuration.getBeanFactory()).registerBeanDefinition("TestBeanSingletonBug",beanDefinitionSingleton);
TestSingletonBeanBug singletonBean = (TestSingletonBeanBug) configuration.getBean("TestBeanSingletonBug");
count(configuration, singletonBean.getInnerBean());
}
}
private void count(ListableBeanFactory factory, TestInnerBean tib) {
System.out.println("Total number of beans: " + factory.getBeanDefinitionCount());
System.out.println("Total number of TestInnerBean definitions: " + factory.getBeanNamesForType(TestInnerBean.class,true, false).length);
System.out.println("ObjectID latest TestInnerBean: " + tib.getCount());
}
private BeanDefinition createTestSingletonBeanBug() throws Exception {
AbstractBeanDefinition beanDefinitionSingleton = BeanDefinitionReaderUtils.createBeanDefinition(null,"com.debug.spring.modified.TestSingletonBeanBug",getClass().getClassLoader());
MutablePropertyValues propssingleton = new MutablePropertyValues();
propssingleton.addPropertyValue("count", "600");
beanDefinitionSingleton.setPropertyValues(propssingleton);
beanDefinitionSingleton.setAbstract(false);
beanDefinitionSingleton.setLazyInit(true);
PropertyValue pv = new PropertyValue("innerBean", createTestInnerBean());
beanDefinitionSingleton.getPropertyValues().addPropertyValue(pv);
return beanDefinitionSingleton;
}
@SuppressWarnings("unused")
private BeanDefinition createTestInnerBean() throws Exception {
objectid++;
AbstractBeanDefinition innerBeanDefinition = BeanDefinitionReaderUtils.createBeanDefinition(null,"com.debug.spring.modified.TestInnerBean", getClass().getClassLoader());
MutablePropertyValues innerbeanproperties = new MutablePropertyValues();
innerbeanproperties.addPropertyValue("count", this.objectid);
innerBeanDefinition.setPropertyValues(innerbeanproperties);
BeanDefinitionHolder bdh = new BeanDefinitionHolder(innerBeanDefinition, TestInnerBean.class.getSimpleName()+ objectid, null);
return innerBeanDefinition;
}
}
class TestInnerBean {
private int count;
public TestInnerBean() {
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
class TestSingletonBeanBug {
private int count;
private TestInnerBean innerBean;
public TestSingletonBeanBug() {
}
public int getCount() {
return count;
}
public TestInnerBean getInnerBean() {
return innerBean;
}
public void setCount(int count) {
this.count = count;
}
public void setInnerBean(TestInnerBean innerBean) {
this.innerBean = innerBean;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-lazy-init="true">
<bean id="TestBeanSingletonBug" singleton="true"
class="com.debug.spring.modified.TestSingletonBeanBug">
<property name="count" value="0"/>
<property name="innerBean">
<bean class="com.debug.spring.modified.TestInnerBean">
<property name="count" value="0"/>
</bean>
</property>
</bean>
</beans>
</code>
Affects: 2.5.4, 2.5.5
Attachments:
- springbug.txt (4.57 kB)
- springbugmodified.txt (4.03 kB)
- springmemoryusingjmap.txt (33.31 kB)