【问题标题】:Is there a memory leak when using StructureMap with Castle.DynamicProxy?将 StructureMap 与 Castle.DynamicProxy 一起使用时是否存在内存泄漏?
【发布时间】:2013-11-03 08:48:46
【问题描述】:

我很难理解我的内存泄漏是从哪里来的。我在研究时遇到了post 的修改版本。我们使用 StructureMap,我们想为 MiniProfiler 使用 AOP,所以这似乎是一个完美的尝试,但是当我实现它时,我开始出现大量内存泄漏。我将泄漏追踪到创建代理的位置。如果我让所有其他代码保持不变,只删除.EnrichWith(...) 调用,内存泄漏就会消失。我正在像这样在 Bootstrapper 类中创建代理类:

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 创建代理有关,因为如果我将代码的所有其他部分保持不变并简单地删除该调用,内存泄漏就会消失。我在这里做错了什么基本的事情吗?

【问题讨论】:

    标签: c# memory-leaks structuremap castle-dynamicproxy


    【解决方案1】:

    我也在使用 Structuremap 和 Castle.Proxy,但方式有点不同。所以,虽然这不是内存泄漏问题的直接答案,但也许它可以给你另一个观点。

    背后的想法是,我们将返回一个承诺,一个wrapper 对象,而不是返回请求的对象。此外,我们正在使用 setter 注入,这确实证明了不仅构造函数注入是有效的概念。首先是调用自定义Convention对象的Structuremap的配置:

    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;
            }
        }
    }
    

    我的类似答案可以在这里找到StructureMap - Circular Dependencies and Lazy Initialization of Setter Properties
    (有一个 Catharsis guidance 用于 VS 2012,创建解决方案,您可以在其中看到它的实际效果)

    【讨论】:

    • 感谢您的回答,有一些非常有趣的事情需要解决。我将尝试修改我们包装类的方式,以模仿你的做法,看看是否仍然存在内存泄漏。
    【解决方案2】:

    如果您查看此similar issue,您会发现他们提倡使用单例ProxyGenerator 实例,以便它重用动态生成的类型。在DynamicProxyHelper 中尝试添加private static readonly ProxyGenerator dynamicProxy = new ProxyGenerator(); 并引用它,而不是每次都更新它。

    【讨论】:

    • 谢谢你的回答,不幸的是,这个项目被搁置到稍后的时间,我没有时间去工作。您提供的链接信息丰富,如果我有时间检查一下,我会回到这个并标记为答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 2011-01-29
    • 2011-05-08
    相关资源
    最近更新 更多