Castle Windsor: Transient Objects and Release Policies

Print Content | More

I’m not a Castle Windsor expert and I started using it heavily some months ago. These days I’m profiling some of the applications I wrote in the past to try to optimize the memory usage and reduce the footprint they allocate.

Using some profiling tools I came across one of the most frequent (and biggest) mistakes you can do while using Castle Windsor with Transient objects: I always forget to call the Release() function from the container from which I requested the object.

This simply happened because Castle do not handle transient DISPOSABLE objects the way I was thinking. In my mind a ‘Transient’ object is (was) something I require from the container and then I have FULL CONTROL over its life, the container (or the factory) should completely forget of the object itself.

Well...it isn’t like that, the default behavior that Castle implements is to keep track of any DISPOSABLE object he creates to deallocate them in the right way when the container itself is disposed.

There are a number of good reasons for this to happen, and it actually is a quite long discussion; you can find some info in these posts:

http://elegantcode.com/2008/12/14/the-component-burden/

http://hammett.castleproject.org/?p=252

some other posts from my friend Alkampfer gives you some more info on the same mistake I made:

http://www.nablasoft.com/Alkampfer/?p=104

http://www.nablasoft.com/Alkampfer/?p=105

http://www.nablasoft.com/alkampfer/index.php/2008/02/29/again-on-castle-transient-and-the-custom-lifecycle/

If, like me, you used some static IoC container that lived along the whole life of the application you can see the huge impact this behavior has on the memory allocated by the application: since the container keeps a reference to any disposable object (even the transient ones) you allocated, they will never be released during a GC action.

So...how to modify the default Castle behavior to match my view? The goal is simple: we want to keep the actual object tracking for all the objects lifestyle types (singleton, pooled, etc...), but we do not want to track transient object; in this way the CLR is able to reclaim the memory they use during a GC call.

It all seemed hard to obtain until I found out that Castle have configurable Release Policies that actually handle the object tracking, here you can find some ‘small’ documentation on the default policies that come with castle: http://www.castleproject.org/container/documentation/trunk/advanced/releasepolicy.html. LifecycledComponentsReleasePolicy is actually the default one.

At first my solution was to implement a new policy that modified the tracking behavior for transient objects:

/// <summary>
/// Inherits from the default ReleasePolicy; do not track our own transient objects.
/// Only tracks components that have decommission steps
/// registered or have pooled lifestyle.
/// </summary>
[Serializable]
public class TrulyTransientReleasePolicy : LifecycledComponentsReleasePolicy
{
    public override void Track(object instance, Burden burden)
    {
        ComponentModel model = burden.Model;
 
        // to modify the way Castle handles the Transient object uncomment the following lines
        if (model.LifestyleType == LifestyleType.Transient)
            return;
 
        base.Track(instance, burden);
    }
}

After some more thinking I decided to modify it a bit and add a custom LifestyleManager class to use toghether with this policy to not alter the default behavior defined by the transient attribute in Castle, here’s my final solution:

/// <summary>
/// a custom Lifestyle, it will inerit from the standard class so if the TrulyTransientReleasePolicy policy
/// isn't used these objects are handled as standard transient objects 
/// </summary>
public class TrulyTransientLifestyleManager : TransientLifestyleManager
{
}
 
/// <summary>
/// Inherits from the default ReleasePolicy; do not track our own transient objects.
/// Only tracks components that have decommission steps
/// registered or have pooled lifestyle.
/// </summary>
[Serializable]
public class TrulyTransientReleasePolicy : LifecycledComponentsReleasePolicy
{
   public override void Track(object instance, Burden burden)
   {
      ComponentModel model = burden.Model;
 
      // to modify the way Castle handles the Transient object uncomment the following lines
      //if (model.LifestyleType == LifestyleType.Transient)
      //   return;
 
      // we skip the tracking for object marked with our custom Transient lifestyle manager
      if ((model.LifestyleType == LifestyleType.Custom) &&
          (typeof(TrulyTransientLifestyleManager) == model.CustomLifestyle))
         return;
 
      base.Track(instance, burden);
   }
}

To ‘force’ Castle to use this policy you have to write some code:

...
_container = new WindsorContainer(
   new XmlInterpreter(new ConfigResource("services")));
_container.Kernel.ReleasePolicy = new TrulyTransientReleasePolicy();
...

Due to the lack of documentation I haven’t found a way to configure this policy using the configuration file, I’ll update this post when I can figure out how to do it.

Here’s how you can define the components in the configuration file

...
<components>
   <!-- test data provider, this one returns some hardcoded data -->
   <component id="DisposableEntity"
              service="IoCRelease.Entities.ITestEntity,IoCRelease"
              type="IoCRelease.Entities.DisposableEntity,IoCRelease"
              lifestyle="transient">
   </component>
   <component id="DisposableEntityTransient"
              service="IoCRelease.Entities.ITestEntity,IoCRelease"
              type="IoCRelease.Entities.DisposableEntity,IoCRelease"
              lifestyle="custom"
              customLifestyleType="IoCRelease.Castle.TrulyTransientLifestyleManager,IoCRelease">
   </component>
   <component id="Entity"
              service="IoCRelease.Entities.ITestEntity,IoCRelease"
              type="IoCRelease.Entities.Entity,IoCRelease"
              lifestyle="transient">
   </component>
</components>
...

The code sample (based on Alkampfer’s code) that comes with this articles presents a couple of test that when executed with the NUnit console will show you what happens using the custom policy:

For the normal LifecycledComponentsReleasePolicy you can see the Dispose is called (the counter gets increment) and we do not have any Finalize call (which was suppressed in the dispose pattern).

IoCRelease1

For the TrulyTransientReleasePolicy you can see that the Dispose is never directly called (the counter remains at 0), while the CLR calls the Finalize when the objects are garbage collected.

IoCRelease2

At this point you have to remember that using the TrulyTransientLifestyleManager and the TrulyTransientReleasePolicy YOU are responsible for the object lifetime, but they are no more anchored to the container and the runtime is able to reclaim their memory if never used.

The good thing is that if you want to implement this behavior in an existing application (to do a quick fix at that huge memory leak you had, while working on a better way to handle/deallocate your objects) all you have to do is to change the Castle configuration file and to inject the policy into the container.

Test project:



Castle windsor, Lifecycledcomponentsreleasepolicy, Releasepolicy, Transient

4 comments

Related Post

  1. #1 da Krzysztof Koźmic - Saturday March 2010 alle 01:15

    If you want to give up the default handing of transient components, you can use NoTrackingReleasePolicy, which is built in, you don't have to roll your own.

  2. #2 da Guardian - Saturday March 2010 alle 01:15

    First of: you have a great blog man, always loved what you write on castle! Backto business: I created this one cause I wanted the default tracking policies for all the object but the one marked as transient. I looked at the implementation of NoTrackingReleasePolicy and I saw that it disables the tracking for any object. I wanted to keep the default tracking for singletons and other lifecycle objects. I wasn't sure how they were handled using the NoTrackingReleasePolicy so I tought that rolling my own was a good solution. By the way, do you know if there's a way to set the release policy using the configuration files? I wasn't able to find any.

  3. #3 da Krzysztof Koźmic - Saturday March 2010 alle 01:15

    I'm not sure, but I guess you can't do that out of the box. Windsor is pretty extensible though, so in 5 minutes you can roll your own facility that will do that if that's what you need.

  4. #4 da forex robot - Monday April 2010 alle 05:42

    Great information! I’ve been looking for something like this for a while now. Thanks!

All fields are required and you must provide valid data in order to be able to comment on this post.


(will not be published)
(es: http://www.mysite.com)