Reputation: 1043
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
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
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