将StructureMap与Castle.DynamicProxy一起使用时是否存在内存泄漏
本文关键字:是否 存在 内存 泄漏 StructureMap Castle DynamicProxy 一起 | 更新日期: 2023-09-27 18:36:48
我正在努力理解我的内存泄漏来自哪里。 我有一个修改版的帖子,我在研究时遇到了。 我们使用StructureMap,我们希望将AOP用于MiniProfiler,所以这似乎是一件完美的尝试,但是当我实现它时,我开始出现大量内存泄漏。 我追踪泄漏到我正在创建代理的地步。 如果我让所有其他代码保持不变并删除.EnrichWith(...)
调用,则内存泄漏就会消失。 我正在引导程序类中创建代理类,如下所示:
x.For<IServiceInterface>()
.Use<ServiceClass>()
.EnrichWith(ex => DynamicProxyHelper.
CreateInterfaceProxyWithTargetInterface(typeof(IServiceInterface), ex));
动态代理帮助程序类:
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;
}
}
和 MyInterceptor 类:
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";
}
}
我相信这与通过调用EnrichWith
创建代理有关,因为如果我保持代码的所有其他部分相同并简单地删除该调用,内存泄漏就会消失。 我在这里做错了什么根本吗?
如果你看一下这个类似的问题,你可以看到他们提倡使用单例ProxyGenerator
实例,以便它重用动态生成的类型。在里面DynamicProxyHelper
尝试添加private static readonly ProxyGenerator dynamicProxy = new ProxyGenerator();
并引用它,而不是每次都更新它。
我也在使用Structuremap和Castle.Proxy,但方式有点不同。因此,虽然这不是内存泄漏问题的直接答案,但也许它可以为您提供另一种观点。
背后的想法是,我们将返回一个承诺,一个wrapper
对象,而不是返回请求的对象。此外,我们使用的是 setter 注入,这确实证明了不仅构造函数注入是有效的概念。首先,有一个调用自定义约定对象的结构图配置:
x.Scan(s =>
{
s.Convention<ProxyConvention>();
s.WithDefaultConventions();
}
..
x.SetAllProperties(.. // configure setter injeciton
代理约定确实注入了包装器,作为实现:
[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
...
所以,现在,我们已经使用ProxInstance
对象为我们的接口注入了实现。有内容:
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;
}
正如我们所看到的,目前,我们确实创建了一个带有城堡的代理,并用 Interceptors
.Wrapper
对象负责实例化真实对象,仅在第一次触摸它的那一刻。因此,循环引用不是问题,实际上它们是受欢迎的:
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;
}
}
}
我的类似答案可以在这里找到 结构图 - 循环依赖和 setter 属性
的延迟初始化(VS 2012有一个宣泄指南,创建解决方案,您可以在其中看到它的实际效果)