Ninject缓存对象,应该被释放,会导致内存泄漏?

5

在Ninject中,当WeakReferenceCollection内没有对象被处理时会出现内存泄漏,我错在哪里了?或者是Ninject存在一个巨大的错误吗?

当Kernal被处理掉后,来自WeakReferenceCollection的所有引用都被处理掉,但是当处于此循环中时,即使代码中没有引用,内存也会暴增。

public class Program
{
    private StandardKernel kernel;

    private static void Main(string[] args)
    {
        new Program().Run();
    }

    public void Run()
    {
        kernel = new StandardKernel(new NinjectSettings() { LoadExtensions = false });
        kernel.Bind<Root>().ToSelf();
        kernel.Bind<Foo>().ToSelf().InCallScope();

        while (true)
        {
            Process();

            Thread.Sleep(500);

            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

    public void Process()
    {
        Root root = kernel.Get<Root>();
        Root root2 = kernel.Get<Root>();
    }

    public class Root
    {
        private Foo _test;

        public Root(Foo foofac)
        {
            Id = Guid.NewGuid();
            _test = foofac;
        }

        protected Guid Id { get; set; }
    }

    public class Foo
    {
    }
}

更新

我已经尝试使用命名作用域,甚至像示例中那样使用工厂: http://www.planetgeek.ch/2012/04/23/future-of-activation-blocks/ 但是在内存分析器中,弱引用集合会随着时间的推移而不断增长,这并不好... 我当前的测试代码是:

public class Program
{
    private StandardKernel kernel;

    private static void Main(string[] args)
    {
        new Program().Run();
    }

    public void Run()
    {
        kernel = new StandardKernel(new NinjectSettings() { LoadExtensions = false });
        kernel.Load<FuncModule>();

        kernel.Bind<IRootFactory>().ToFactory();
        var scopeParameterName = "scopeRoot";
        kernel.Bind<Root>().ToSelf().DefinesNamedScope(scopeParameterName);
        kernel.Bind<Foo>().ToSelf().InNamedScope(scopeParameterName);
        

        while (true)
        {
            Process();

            GC.Collect();
            GC.WaitForPendingFinalizers();

            Thread.Sleep(500);
        }
    }

    public void Process()
    {
        Root root = kernel.Get<IRootFactory>().CreateRoot();
        Root root2 = kernel.Get<IRootFactory>().CreateRoot();
    }

    public class Root
    {
        private Foo _test;

        public Root(Foo foofac)
        {
            Id = Guid.NewGuid();
            _test = foofac;
        }

        protected Guid Id { get; set; }
    }

    public class Foo
    {
    }

    public  interface IRootFactory
    {
        Root CreateRoot();
    }
}
2个回答

1

需要时间,且不意味着它是确定性的-请参考实现代码以了解实际情况,缓存和收集文章以了解精神。

首先,在您的示例中,您应该等待触发Ninject释放机制(通过定时器)。它确实做不到的一件事是在GC发生时同步Dispose和Release。

底线是,最好通过IResolutionRoot.ReleaseInRequestScope相关联的命名范围确定地执行释放。


好的,但是这可能是Ninject中的一个错误,在我完成Root作为上下文后它没有被处理,即使在Ninject中每30秒调用一次GarbageCollectionCachePruner也无法处理弱引用,因为它们仍然存活,所以它们在某个地方仍然被引用。我考虑使用BeginBlock方法,它似乎可以解决问题 - 但是在我的代码中,我总是需要创建一个UnitOfWork对象,这会增加更多的工作量,只是为了确保Ninject释放它们,而本应该在上下文对象不再被引用时自动完成! - eble
你有CreateNamedScope相关的测试可以让我查看吗? 感谢你的回复,听起来你已经尝试了一些东西并且学到了一些东西 :) - eble
看看我的更新代码,这里我使用了命名作用域和工厂,就像你给出的示例一样,但仍然没有清理干净。 - eble
@eble 你可以以任何方式查看内容,关键是:DefinesNamedScope和stuff与作用域有关。如果某人没有进行显式的“Release”或“Dispose”作用域,则会发生随机事件。我知道依赖于这些随机事件对我来说行不通,因此我使用以下作用域:- Transient:无泄漏 Singleton:无泄漏,在内核被Dispose时消失 Request:无泄漏,在请求结束时消失(通过EndRequest或我的using( ... CreateNamedScope())。在那之前,我依赖于[WCF] InScope(OperationContext.Current)而没有清理 - 泄漏。 - Ruben Bartelink
@eble 我并不惊讶你的代码片段都不能按照你的意愿工作,我也不会使用它们。我的建议方法 a) 基于根本原因而有效 b) 已经使用多年 c) 有意被其他人采用。 - Ruben Bartelink
显示剩余7条评论

1

虽然我不建议使用它,但GC.Collect()是一种异步机制。只有调用它才会触发GC,它不会等待GC完成。如果您非常关注它,您可能需要调用GC.WaitForPendingFinalizers()


你说得没错,即使使用了WaitForPendingFinalizers仍然存在问题。 - eble
为了清晰起见,GC等将释放Ninject内部的“弱引用”,但是当它对此做出反应时,其实现取决于它自己。 - Ruben Bartelink
不,如果您使用我的代码进行调试,WeakReference 仍然是活动的。 - eble
2
您可以在 GarbageCollectionCachePruner.PruneCacheIfGarbageCollectorHasRun 中看到这一点。 - eble

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接