akousmata
akousmata

Reputation: 1043

Is there a memory leak when using StructureMap with Castle.DynamicProxy?

I am struggling to understand where my memory leak is coming from. I have a modified version of a post I came across while researching. We use StructureMap, we want to use AOP for MiniProfiler so this seemed like a perfect thing to try but when I implemented it, I started get massive memory leaks. I tracked down the leak to the point where I am creating the proxy. If I leave all of the other code the same and just remove the .EnrichWith(...) call, the memory leak goes away. I am creating the proxy class in a Bootstrapper class like so:

x.For<IServiceInterface>()
 .Use<ServiceClass>()
 .EnrichWith(ex => DynamicProxyHelper.
     CreateInterfaceProxyWithTargetInterface(typeof(IServiceInterface), ex));

The Dynamic Proxy Helper Class:

public class DynamicProxyHelper
{
    public static IEnumerable<Type> GetScannableTypes()
    {
        var types = Assembly.GetExecutingAssembly().GetTypes();
        var scannableTypes = new List<Type>();
        foreach (var type in types)
        {
            // http://www.hanselman.com/blog/DoesATypeImplementAnInterface.aspx
            if (typeof (IAttributeScanTask).IsAssignableFrom(type)
                && type.FullName != typeof (IAttributeScanTask).FullName)
            {
                scannableTypes.Add(type);
            }
        }

        return scannableTypes;
    }

    public static object CreateInterfaceProxyWithTargetInterface<T>(Type interfaceType, T obj) 
    {
        if (!interfaceType.IsInstanceOfType(obj))
        {
            throw new ArgumentException(
                "DynamicProxyHelper: Object passed to the proxy must inherit from the interface type passed to the proxy.");
        }

        // Create the proxy and return the result
        var dynamicProxy = new ProxyGenerator();
        var scannableTypes = GetScannableTypes();

        var result = dynamicProxy.CreateInterfaceProxyWithTargetInterface(
            interfaceType,
            obj,
            new IInterceptor[] { new MyInterceptor(obj.GetType(), new AttributeScanEngine(), scannableTypes)} );

        return result;
    }
}

And the MyInterceptor Class:

public interface IMyInterceptor : IInterceptor {}
public class MyInterceptor : IMyInterceptor
{
    private readonly Type _concreteType;
    private readonly IAttributeScanEngine _scanEngine;
    private readonly IEnumerable<Type> _scannableTypes;
    private const string AttributeNameSpace = "MyAttributes";


    public MyInterceptor() : this(typeof(object), new AttributeScanEngine(), new List<Type>()){}

    public MyInterceptor(Type concreteType, IAttributeScanEngine scanEngine, IEnumerable<Type> scannableTypes)
    {
        _concreteType = concreteType;
        _scanEngine = scanEngine;
        _scannableTypes = scannableTypes;
    }

    public void Intercept(IInvocation invocation)
    {
        var scanType = ResolveScanType(invocation);

        // We found a matching attribute that can be applied
        if (scanType != null)
        {
            // execute the custom task we need to run
            _scanEngine.Run(invocation, scanType, _concreteType);
        }
        else
        {
            // no scanned types could be found so execute the method as is
            invocation.Proceed();
        }

    }

    protected internal virtual Type ResolveScanType(IInvocation invocation)
    {
        foreach (var type in _scannableTypes)
        {
            var attributeName = GetAttributeName(type.Name);
            var attributeType = Type.GetType(attributeName);
            if (attributeType != null)
            {
                var attributeDecoration = Attribute.GetCustomAttribute(invocation.GetConcreteMethodInvocationTarget(), attributeType, true);

                // We found an attribute for this scan type
                if (attributeDecoration != null)
                {
                    return type;
                }
            }
        }

        return null;
    }

    protected internal virtual string GetAttributeName(string typeName)
    {
        var aspectName = typeName.Substring(0, typeName.IndexOf("ScanTask"));
        return AttributeNameSpace + "." + aspectName + "Attribute";
    }         
}

I believe it's related to the creation of the proxy via the call to EnrichWith because if I leave all other parts of the code the same and simply remove that call, the memory leak goes away. Is there something fundamental I'm doing wrong here?

Upvotes: 1

Views: 1142

Answers (2)

NickL
NickL

Reputation: 1960

If you look at this similar issue you can see that they advocate using a singleton ProxyGenerator instance so that it reuses dynamically generated types. Inside DynamicProxyHelper try adding private static readonly ProxyGenerator dynamicProxy = new ProxyGenerator(); and reference that instead of newing it up each time.

Upvotes: 3

Radim K&#246;hler
Radim K&#246;hler

Reputation: 123861

I am using Structuremap and Castle.Proxy as well, but in a bit different way. So, while this is not the direct answer to the memory leak issue, maybe it could give you another view.

The idea behind is, that instead of returning the requested objects, we will return a promise, a wrapper object. Also, we are using the setter injection, which does prove the concept that not only constructor injection is valid. First of all, there is a configuration of the Structuremap calling custom Convention object:

x.Scan(s =>
{
    s.Convention<ProxyConvention>();
    s.WithDefaultConventions();
}
..
x.SetAllProperties(.. // configure setter injeciton

The Proxy Convention, does inject the Wrapper, as the implementation:

[CLSCompliant(false)]
public class ProxyConvention : DefaultConventionScanner
{
    public override void Process(Type type, Registry registry)
    {
        var interfacesToHandle = type.GetInterfaces()
             .Where(i => i... // select what which interface should be mapped

        foreach (var inter in interfacesToHandle)
        {
            var setting = registry
                .For(inter)
                .HybridHttpOrThreadLocalScoped();
                .Use(new ProxyInstance(type)); // here we go to inject wrapper
     ...

So, now, we have injected implementation for our interfaces using ProxInstance object. There is the content:

public ProxyInstance(Type type)
{
    ConcreteType = type; // the type for our Wrapper, the real implementation
}
protected override object build(Type pluginType, BuildSession session)
{
    var aopFilters = 
         // my custom way how to inject more AOP filters
         AopFilterManager.GetFilters()
         // the core for us, one of the interceptors is our Wrapper
        .Union(new[] { new Wrapper(ConcreteType) })
        .ToArray();

    // Castle will emit a proxy for us, but the Wrapper will do the job
    var proxy = Factory
         .CreateClassProxy(ConcreteType, AopFilterManager.AopOptions, aopFilters);

    return proxy;
}

As we can see, at this moment, we do have created a Proxy with a Castle, enriching it with Interceptors. The Wrapper object, is responsible for instantiating the real object, only in the moment when it is firstly touched. Therefore, the circular references are not an issue, in fact they are welcome:

public class Wrapper : IInterceptor
{
    object _lazy;
    protected readonly Type Type;

    public Wrapper(Type type)
    {
        Type = type;
    }

    public void Intercept(IInvocation invocation)
    {
        if (_lazy.IsNull()) // lazily instantiate the instance
        {
            _lazy = ObjectFactory.GetInstance(Type);
        }
        try
        {
            var method = invocation.Method;
            if (method.ContainsGenericParameters)
            {
                method = method.MakeGenericMethod(invocation.GenericArguments);
            }
            invocation.ReturnValue = method.Invoke(_lazy, invocation.Arguments);
        }
        catch (TargetInvocationException ex)
        {
            // PublishingManager.Publish(.... // publish exception
            throw;
        }
    }
}

My similar answer could be found here StructureMap - Circular Dependencies and Lazy Initialization of Setter Properties
(There is a Catharsis guidance for VS 2012, creating solution, where you can see it in action)

Upvotes: 0

Related Questions