简体   繁体   English

NInject 3泄漏(有一个示例项目),是否可以强制移除对象?

[英]NInject 3 leaks (there's a sample project), is it possible to force removal of objects?

I created a sample project to model what I faced in my real project. 我创建了一个示例项目来对我在实际项目中面临的情况进行建模。 The problem is that when I query some external data in a cycle (in the real life my project queries windows to find match(es), and stops querying on success or on timeout expiration). 问题是,当我循环查询某些外部数据时(在现实生活中,我的项目会查询窗口以查找匹配项,并在成功或超时到期时停止查询)。 It seems to me that NInject does not dispose created objects and consider the cycle as one long call. 在我看来,NInject不会处置创建的对象,而是将循环视为一次长调用。 How to work around this situation? 如何解决这种情况? Maybe, cut out the code that fills list and put it into another class? 也许,切出填充列表的代码并将其放入另一个类中? Or simply, could you make my sample project better? 或者简单地说,您可以使我的示例项目更好吗?

The link on Github or its code below: 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);
    }
}

I fixed the problem with memory by using ChildKernel. 我通过使用ChildKernel解决了内存问题。 At the moment, I can't say anything about performance, however the memory leak is definitely fixed: the project started at 10MB and works on 15-25MB. 目前,我还不能说任何有关性能的信息,但是内存泄漏肯定是固定的:该项目开始于10MB,工作于15-25MB。 Anyway, I'd be glad to consider other alternatives to my solution if you, the stackoverflowers, have such. 无论如何,如果您的stackoverflowers拥有这样的解决方案,我很乐意考虑其他替代方案。 My solution is here ( Github ) and the below: 我的解决方案是在这里( 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);
    }
}

If think there is a misconception here. 如果认为这里有一个误解。 I think InCallScope is not doing what you think it is. 我认为InCallScope并未按照您的想法去做。 Have a look at the following integration test for 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();

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

As you can see the IChild is instantiated in the scope of Parent . 如您所见, IChildParent范围内被实例化。 IChild is disposed as soon as Parent is disposed. IChild被尽快安置Parent配置。

In your example, Requester is bound InSingletonScope . 在您的示例中, Requester绑定了InSingletonScope Everything which is instantiated for / by Requester in its InChildScope will only be disposed once Requester is disposed. Requester在其InChildScope为/实例化的所有内容都只会在Requester被处置后处置。 However, since you don't seem to be using ContextPreservation in your original Requester there is actually not even a scope for your IMyObj . 但是,由于您似乎没有在原始Requester使用ContextPreservation,因此实际上甚至没有IMyObj

How would you like it to work? 您希望它如何运作?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM