【问题标题】:NInject 3 leaks (there's a sample project), is it possible to force removal of objects?NInject 3 泄漏(有一个示例项目),是否可以强制移除对象?
【发布时间】:2014-01-22 12:19:07
【问题描述】:

我创建了一个示例项目来模拟我在实际项目中遇到的问题。 问题是,当我在一个循环中查询一些外部数据时(在现实生活中,我的项目会查询窗口以查找匹配项,并在成功或超时到期时停止查询)。在我看来,NInject 并没有处理创建的对象,而是将循环视为一个长时间的调用。 如何解决这种情况?也许,剪下填充列表的代码并将其放入另一个类?或者简单地说,你能让我的示例项目变得更好吗?

The link on Github或其代码如下:

public interface IMyObj : IDisposable
{
    string Name { get; set; }
}

public class MyObj : IMyObj
{
    public virtual string Name { get; set; }

    public virtual void Dispose()
    {
        Name = string.Empty;
        GC.SuppressFinalize(this);
    }
}

public class NjModule : NinjectModule
{
    public override void Load()
    {
        Bind<IMyObj>()
            .To<MyObj>()
            .InCallScope();

        Bind<Requester>()
            .ToSelf()
            .InSingletonScope();
    }
}

public class Requester
{
    public List<IMyObj> RequestObjects()
    {
        List<IMyObj> list = new List<IMyObj>();

        for(int i = 0; i < 10; i++) {
            var myObj = Program.Kernel.Get<IMyObj>();
            myObj.Name = "abcdefghijklmnopqrstuvwxyz";
            list.Add(myObj);
        }

        return list;
    }
}

class Program
{
    public static IKernel Kernel;

    public static void Main(string[] args)
    {
        Console.WriteLine("Hello NInject!");

        // TODO: Implement Functionality Here

        Kernel = new StandardKernel(new NjModule());
        Kernel.Settings.ActivationCacheDisabled = true;

        var requester = Kernel.Get<Requester>();

        for (int i = 0; i < 100000000; i++) {

            List<IMyObj> list =
                requester.RequestObjects();

            foreach (MyObj listItem in list) {
                listItem.Dispose();
            }
            list.Clear();
            list = null;

        }

        Console.Write("Press any key to continue . . . ");
        Console.ReadKey(true);
    }
}

【问题讨论】:

    标签: c# memory scope c#-3.0 ninject


    【解决方案1】:

    我使用 ChildKernel 解决了内存问题。目前,我不能说任何关于性能的事情,但是内存泄漏肯定是固定的:该项目从 10MB 开始,在 15-25MB 上运行。 无论如何,如果您(stackoverflowers)有这样的解决方案,我很乐意考虑我的解决方案的其他替代方案。 我的解决方案在这里 (Github) 和以下:

    public interface IMyObj : IDisposable
    {
        string Name { get; set; }
    }
    
    public class MyObj : IMyObj
    {
        public virtual string Name { get; set; }
    
        public virtual void Dispose()
        {
            Name = string.Empty;
            GC.SuppressFinalize(this);
        }
    }
    
    public class NjModule : NinjectModule
    {
        public override void Load()
        {
            Bind<Requester>()
                .ToSelf()
                .InSingletonScope();
    
            Bind<IChildKernel>().ToSelf().InSingletonScope();
        }
    }
    
    public class NjChildKernelModule : NinjectModule
    {
        public override void Load()
        {
            Bind<IMyObj>()
                .To<MyObj>()
                .InCallScope();
        }
    }
    
    public class Requester
    {
        public List<IMyObj> RequestObjects(IChildKernel childKernel)
        {
            List<IMyObj> list = new List<IMyObj>();
    
            for(int i = 0; i < 10; i++) {
                var myObj = childKernel.Get<IMyObj>();
                myObj.Name = "abcdefghijklmnopqrstuvwxyz";
                list.Add(myObj);
            }
    
            return list;
        }
    }
    
    class Program
    {
        public static IKernel Kernel;
    
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello NInject!");
    
            // TODO: Implement Functionality Here
    
            Kernel = new StandardKernel(new NjModule());
            Kernel.Settings.ActivationCacheDisabled = true;
    
            var requester = Kernel.Get<Requester>();
    
            for (int i = 0; i < 100000000; i++) {
    
                var childKernel = new ChildKernel(Kernel, new NjChildKernelModule());
                childKernel.Settings.ActivationCacheDisabled = true;
    
                List<IMyObj> list =
                    requester.RequestObjects(childKernel);
    
                foreach (MyObj listItem in list) {
                    listItem.Dispose();
                }
                list.Clear();
                list = null;
    
                childKernel.Dispose();
            }
    
            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
    

    【讨论】:

    • 最后,我在我的项目中实现了这种方法,并且成功了。我必须将所有绑定移动到我的对象,绑定到它们的属性,绑定到通过 Castle.DynamicProxy 佩戴的接口,绑定到填充这些接口的属性和方法后面的对象......其中一些是 InSingletonScope 和我必须制作 InCallScope 并进入子内核的模块以使整个解决方案正常工作。尽管如此,我还是希望看到另一个实现——正如我所听到的,可以通过 ContextPreservation 和/或工厂扩展重新实现它......
    【解决方案2】:

    如果认为这里有误解。 我认为InCallScope 没有按照您的想法行事。查看InCallScope 的以下集成测试:

    this.kernel.Bind<Parent>().ToSelf();
    this.kernel.Bind<Child>().ToSelf().InCallScope();
    this.kernel.Bind<IGrandChild>().To<GrandChild>().InCallScope();
    
    var parent1 = this.kernel.Get<Parent>();
    var parent2 = this.kernel.Get<Parent>();
    parent1.Dispose();
    
    parent1.FirstChild.Should().BeSameAs(parent1.SecondChild);
    parent1.GrandChild.Should().BeSameAs(parent1.FirstChild.GrandChild);
    parent1.FirstChild.Should().NotBeSameAs(parent2.FirstChild);
    parent1.GrandChild.Should().NotBeSameAs(parent2.GrandChild);
    
    parent1.FirstChild.IsDisposed.Should().BeTrue();
    parent1.FirstChild.GrandChild.IsDisposed.Should().BeTrue();
    parent2.FirstChild.IsDisposed.Should().BeFalse();
    parent2.FirstChild.GrandChild.IsDisposed.Should().BeFalse();
    
    parent2.Dispose();
    parent2.FirstChild.IsDisposed.Should().BeTrue();
    parent2.FirstChild.GrandChild.IsDisposed.Should().BeTrue();
    

    (来源:https://github.com/ninject/ninject.extensions.namedscope/blob/master/src/Ninject.Extensions.NamedScope.Test/NamedScopeIntegrationTest.cs

    如您所见,IChildParent 的范围内实例化。 IChild 会在 Parent 被释放后立即释放。

    在您的示例中,Requester 绑定了InSingletonScope。由Requester 在其InChildScope 中为/ 实例化的所有内容只会在Requester 被处置后被处置。 但是,由于您似乎没有在原始 Requester 中使用 ContextPreservation,因此实际上甚至没有您的 IMyObj 的范围。

    您希望它如何工作?

    【讨论】:

    • 我需要什么:我的代码在超时期间搜索对象(窗口、控件等)。它获取许多相似的对象并应用规则(名称、类型等)。 - 如果有一个或多个匹配,则完成搜索并将结果作为集合返回。 - 如果搜索正在进行,所有不匹配的对象应在检查后立即处理。 -在超时到期时,所有对象也应被处置。 - 匹配并返回的所有搜索中的所有对象都应该存在,直到用户销毁它们。在这种情况下,最好的方法是什么?
    • 简而言之,我需要长时间只拥有那些自动匹配并销毁所有其他对象的对象。
    • 你能把“匹配”部分移到分辨率/绑定中吗?有条件绑定还是有分辨率约束?还是真的需要在匹配完成之前实例化对象?在后一种情况下,您需要滚动自己的实现(基本上就像您对子内核所做的那样)。但是,如果您可以将其移动到绑定/请求中,则可以使用范围。
    猜你喜欢
    • 1970-01-01
    • 2012-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    相关资源
    最近更新 更多