Leo Liang opened SPR-7502 and commented
Since Spring3.0.0, Spring adds a static ConcurrentHashMap named serializableFactories in DefaultListableBeanFactory to support the BeanFactory's serialization. But we found that when we create more and more bean factories in our application, there seems a memory leak. After examined the DefaultListableBeanFactory's source, We found that spring uses a WeakReference to reference the serializableFactories' value. But actually, the segement could not be garbage collected even its weak reference value has been garbage collected.
Affects: 3.0.1
Attachments: - mylyn-context.zip (207.18 kB)
Referenced from: commits https://github.com/spring-projects/spring-framework/commit/b72cca54030d380ab70af0eed60d82074e2e3522
Comment From: spring-projects-issues
Leo Liang commented
'segement' in the last sentence is segment belong to the serializableFactories
Comment From: spring-projects-issues
Juergen Hoeller commented
What's the lifecycle of those DefaultListableBeanFactory instances that you are creating? Are you shutting them down as well, which should actually remove them from the serializableFactories Map?
Juergen
Comment From: spring-projects-issues
Leo Liang commented
I admit that one of our developers did use the context in an improper way(all the DefaultListableBeanFactory instances are created as local variable in some method), but since the map is static, even we close the context, its entry still can't be gc(although the value is weakreference), except that the app exits, do I miss something?
Comment From: spring-projects-issues
Chris Beams commented
Hi Leo,
We have identified and reproduced a specific set of conditions that can cause the leak you've reported. However, it's a pretty unlikely scenario. Let me explain:
The following will produce a leak:
while (true) {
new GenericApplicationContext();
}
As will this variation:
while (true) {
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.close();
}
However, if the context is properly refresh()
'ed before close()
, memory consumption stays constant:
while (true) {
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.refresh();
ctx.close();
}
So in short, there is a bug here which we'll fix promptly, but it must be asked -- are you indeed creating thousands of application contexts without refreshing them, and if so, why? Note that most ApplicationContext implementations implicitly call refresh()
for you when you call a constructor that takes arguments such as a path to a Spring XML file.
Comment From: spring-projects-issues
Leo Liang commented
This code is written by a junior developer. After we found memory leak bug, we have fixed this bug in our app.(let the context be created once not every time the method is called). Thank you. :-)
Comment From: spring-projects-issues
Leo Liang commented
And I really forgot to check the close()'s source. After checked the source, exactly it will not cause a leak if we call close() after properly refresh()ed. :-)
Comment From: spring-projects-issues
Chris Beams commented
Ah, great - mystery solved, then!
In the meantime, this is being fixed. Thanks for the report.
Comment From: spring-projects-issues
Chris Beams commented
Resolved. Leo, thanks again for catching this - inadvertent as it may have been.
GenericApplicationContext and AbstractRefreshableApplicationContext implementations now call DefaultListableBeanFactory.setSerializationId() only upon successful refresh() instead of on instantiation of the context, as was previously the case with GAC.
DLBF.setSerializationId() adds the beanFactory to the static DLBF.serializableFactories map, and while calling close() on the application context removes entries from that map, it does so only if the context is currently active (i.e. refresh() has been called).
Also, cancelRefresh() has been overridden in GAC just as it has been in ARAC to accomodate the possibility of a BeansException being thrown. In this case, the beanFactory serializationId will be nulled out and the beanFactory removed from the serializableFactories map.
The SerializableBeanFactoryMemoryLeakTests test case provides full coverage of these scenarios.
Comment From: spring-projects-issues
Malathi commented
Can somebody answer the reason for this issue in layman terms here: http://stackoverflow.com/questions/28271654/spring-creating-multiple-applicationcontext-object-causes-memory-leak
Comment From: hassibnkhoury
so what is the solution? i am using the latest version of spring boot 3.2.1 and still the issue of memory appears: One instance of “org.springframework.beans.factory.support.DefaultListableBeanFactory” loaded by “org.springframework.boot.loader.launch.LaunchedClassLoader @ 0xf006e300” occupies 1,025,792 (11.57%) bytes. The memory is accumulated in one instance of “org.springframework.beans.factory.support.DefaultListableBeanFactory”, loaded by “org.springframework.boot.loader.launch.LaunchedClassLoader @ 0xf006e300”, which occupies 1,025,792 (11.57%) bytes.
Keywords org.springframework.beans.factory.support.DefaultListableBeanFactory org.springframework.boot.loader.launch.LaunchedClassLoader