Poor performance of XmlApplicationContext.Dispose()
Of all my application objects (all 1400+ of them) that are registered/created through Spring, only a small percentage of them define cleanup code. In other words, only about 2% of my application objects either define IDisposable or a "destroy-method" method. And for those that do the cleanup code is very lightweight. Yet when Dispose() is invoked on the XmlApplicationContext it takes upwards of 60 seconds to destroy all 1400+ objects. This comes out to about 45ms per object and makes for a very slow application shutdown. Looking at the logs, there isn't any one single object responsible for the delay -- all objects take an equal amount of time to be destroyed.
Is there something obvious I am missing -- a simple configuration change perhaps? I can't think of anything that would cause this kind of performance issue. I am currently on version 1.3.1 and running in a .NET 4.0 environment.
Spring is not scalable
The problem appears to be the scalability of Spring. The more objects you define in Spring the worse the performance gets when disposing a Spring context. In my application the main object factory in Spring has 2956 object definition names. During disposal the factory destroys dependent objects, recursively looping through the object hierarchy and disposing/destroying each applicable object. The problem is that this recursion takes an eternity and makes disposing the context impractical.
The change I tinkered with that improved the performance of disposal is to destroy dependent objects only if the current object is itself disposable/destroyable. This resulted in a 75% improvement in performance. The only thing stopping me from getting a larger improvement are AbstractFactoryObject objects. The call to destroy dependent objects is likely wasted on these objects because even though they implement IDisposable, the inner singleton they forward the call onto may not implement IDisposable. If I could fix this problem I could reduce the time to call Dispose to almost nothing.
This is an interesting approach to increasing the disposal performance; would you be willing to submit a github pull request that we could examine? This (general) issue is probably related to https://jira.springsource.org/browse/SPRNET-1318 and we are aware that the present dispose algorithm is a possible performance bottleneck and would be interested in seeing your approach to solving it if you're willing to share it.
The notes in the defect you referenced talk about a more appropriate fix -- using separate factories to track destroy-able objects. Unfortunately, the fix I came up with (below) partially relies on hard coding a factory type -- a hack if you will -- that isn't worthy of submission. Here's what I experimented with (instrumentation removed for clarity):
protected override void DestroyObject(string name, object target)
if (target is IDisposable)
ObjectFactoryCreatingFactoryObject factory = target as ObjectFactoryCreatingFactoryObject;
// destroy factory only if it requires it
if (factory != null && factory.IsSingleton && !(factory.GetObject() is IDisposable))
RootObjectDefinition rootDefinition = GetMergedObjectDefinition(name, false);
if (rootDefinition != null && StringUtils.HasText(rootDefinition.DestroyMethodNa me))
InvokeCustomDestroyMethod(name, target, rootDefinition.DestroyMethodName);
Understood -- thanks for the sample code all the same. Bottom line: we know about this bottleneck and are targeting to address it in the next release. Thanks again!
We just ran into the same issue when creating more than a few hundred objects. I ran a test on some simple objects with spring creating (10K) and "disposing" of them took over 10 minutes on a really nice machine.
I am not trying to "rub salt" on the wound since you guys already know about the issue.
I just was checking on the status of this. I saw the target for the release had passed.
Maybe some other alternatives that would be okay to use and faster to code would be:
1. Like the previous post - disable Idisp checks from a flag in the xml file... no need to do the check if you know you are not using any class the imps Idisposable.
2. Having a marker interface for parent classes that you want to dispose of - that might hold or "own" Idisposable objects.
3. Separate thread?
Tags for this Thread