Search code examples
c#asp.net-mvcmemory-leakscastle-windsordynamic-proxy

Castle windsor proxy generate memory leak


Castle windsor is used in MVC application as it is described here: Plugging Windsor in MVC

In my application there is one difference and that is method AddControllerLoggingFunctionality:

var controller = ((IController)container.Kernel.Resolve(controllerType)).AddControllerLoggingFunctionality();

This method is in logger class:

[DebuggerStepThrough]
public static class Logger
{
    private static readonly Castle.DynamicProxy.ProxyGenerator proxyGenerator;
    static Logger()
    {
        proxyGenerator = new Castle.DynamicProxy.ProxyGenerator();
        Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(
            typeof(ServiceContractAttribute));
    }

    public static TInterface AddControllerLoggingFunctionality<TInterface>(this TInterface implementation)
        where TInterface : class
    {
        if (implementation == null)
        {
            throw new ArgumentNullException("implementation");
        }

        if (!typeof(TInterface).IsInterface)
        {
            throw new Exception("Type of 'TInterface' must be interface.");
        }

        Castle.DynamicProxy.ProxyGenerationOptions options =
            new Castle.DynamicProxy.ProxyGenerationOptions();

        var origAttribs = implementation.GetType().GetCustomAttributesData();
        if (origAttribs != null)
        {
            foreach (var origAttrib in origAttribs)
            {
                options.AdditionalAttributes.Add(
                    AttributeUtil.CreateBuilder(origAttrib));
            }
        }

        return (TInterface)proxyGenerator.CreateInterfaceProxyWithTarget<TInterface>(
            implementation,
            options,
            new ControllerLoggingInterceptor(implementation.GetType()));
    }
}

And Can someone explain this? Why IController can call AddControllerLoggingFunctionality and what does it?

Because of this change, this controllers are never released from memory(when container.Kernel.ReleaseComponent(controller); is called) and I get memory leak. The "Object tracked by release policy" counter increase all the time. If i remove AddControllerLoggingFunctionality, then "Object tracked by release policy" counter decrease each time when I call ReleaseComponent and memory leak doesn't happen.


Solution

  • You aren't call Release() on the controller but are calling it on the proxy you manually created so Release() is just a no-op to Windsor as it doesn't know about that object and so keeps tracking the controller component.

    If you use Windsor's built-in interception support you don't have to worry about this problem as Windsor will know how to dispose of the component when passed its own internally managed proxy.

    If you want to test this before changing to Windsor's built-in support, cast your proxy to Castle.DynamicProxy.IProxyTargetAccessor and call DynProxyGetTarget() to get your controller instance which you'll need to pass to Release().

    (This answer is copied from our discussion on the Castle Users mailing list)