NamedScope和垃圾收集 [英] NamedScope and garbage collection

查看:281
本文介绍了NamedScope和垃圾收集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(这个问题最早是在Ninject谷歌集团问过,但我现在看到,#2似乎更有效。)

(This question was first asked in the Ninject Google Group, but I see now that Stackoverflow seems to be more active.)

我使用的NamedScopeExtension到注入相同的视图模型到视图和演示两者。视图已经被释放后,内存分析表明,该视图模型仍然由Ninject缓存保留。我怎样才能让Ninject释放视图模型?

I'm using the NamedScopeExtension to inject the same ViewModel into both the View and the Presenter. After the View have been released, memory profiling shows that the ViewModel is still retained by the Ninject cache. How can I make Ninject release the ViewModel? All ViewModels are released when the Form is Closed and Disposed, but I'm creating and deleting Controls using a Factory in the Form and would like the ViewModels be garbage collected to (the Presenter and View gets collected).

请参阅下面的UnitTest,使用dotMemoryUnit,对于问题的说明:

See the following UnitTest, using dotMemoryUnit, for an illustration of the problem:

using System;
using FluentAssertions;
using JetBrains.dotMemoryUnit;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Ninject;
using Ninject.Extensions.DependencyCreation;
using Ninject.Extensions.NamedScope;

namespace UnitTestProject
{
    [TestClass]
    [DotMemoryUnit(FailIfRunWithoutSupport = false)]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod()
        {
            // Call in sub method so no local variables are left for the memory profiling
            SubMethod();

            // Assert
            dotMemory.Check(m =>
            {
                m.GetObjects(w => w.Type.Is<ViewModel>()).ObjectsCount.Should().Be(0);
            });
        }

        private static void SubMethod()
        {
            // Arrange
            var kernel = new StandardKernel();
            string namedScope = "namedScope";
            kernel.Bind<View>().ToSelf()
                  .DefinesNamedScope(namedScope);
            kernel.DefineDependency<View, Presenter>();
            kernel.Bind<ViewModel>().ToSelf()
                  .InNamedScope(namedScope);
            kernel.Bind<Presenter>().ToSelf()
                  .WithCreatorAsConstructorArgument("view");

            // Act
            var view = kernel.Get<View>();
            kernel.Release(view);
        }
    }

    public class View
    {
        public View()
        {
        }

        public View(ViewModel vm)
        {
            ViewModel = vm;
        }

        public ViewModel ViewModel { get; set; }
    }

    public class ViewModel
    {
    }

    public class Presenter
    {
        public View View { get; set; }
        public ViewModel ViewModel { get; set; }

        public Presenter(View view, ViewModel viewModel)
        {
            View = view;
            ViewModel = viewModel;
        }
    }
}



dotMemory.Check断言失败和分析快照时的视图模型具有到Ninject缓存引用。我想查看发布时指定的范围应该被释放。

The dotMemory.Check assert fails and when analyzing the snapshot the ViewModel has references to the Ninject cache. I thought the named scope should be released when the View was released.

问候,
安德烈亚斯

Regards, Andreas

推荐答案

答案很简单:添加 INotifyWhenDisposed 你的查看。处置的看法。这将导致ninject自动处理约束 InNamedScope 加也ninject将取消引用这些对象的所有东西。这将导致(最终)垃圾收集(除非你挂在其他地方强引用)。

TL;DR

short answer: add INotifyWhenDisposed to your View. Dispose the view. This will lead to ninject automatically disposing all stuff bound InNamedScope plus also ninject will un-reference these objects. This will lead to (eventual) garbage collection (unless you're hanging on to strong references elsewhere).

视图时发布/获取处理的Ninject没有得到通知。
这就是为什么ninject具有运行检查的范围,对象是否还活着(活着=未收集的垃圾)的定时器。如果范围对象是不是活了其配置/释放哪些是在范围举行的所有对象。

Ninject does not get informed when the view is released / get's disposed. That's why ninject has a timer running to check whether the scope-object is still alive (alive = not garbage collected). If the scope-object is not alive anymore it disposes/releases all the objects which were held in scope.

我相信计时器设置为默认30秒。

I believe the timer is set to 30seconds by default.

现在这是什么意思是什么呢?

Now what does this mean exactly?


  • 如果没有内存压力,直到范围,对象是垃圾收集GC可能需要很长的时间(或不会做,永远)

  • 一度的范围对象是垃圾回收,可能需要约30对于范围的对象秒处置并释放以及

  • 一旦ninject发布范围的对象,同样,GC可能需要很长的时间与对象的集合如果没有内存压力。

现在,如果你需要的对象是处置/立即释放范围内被释放时,您将需要添加的 INotifyWhenDisposed 的范围对象(见的这里)。
使用命名范围,则需要这个接口加入到这势必与 DefinesNamedScope 的类型 - 你的情况查看

Now if you need the objects to be disposed/released immediately when the scope is released, you will need to add INotifyWhenDisposed to the scope object (also see here). With named scopes, you'll need to add this interface to the type which is bound with DefinesNamedScope - in your case the View.

据Ninject.Extensions.NamedScope的集成测试,这将足够了:看到的这里

According to Ninject.Extensions.NamedScope's integration tests this will suffice: see here

请注意:这是真的让这个确定性的唯一一件事就是范围内的对象的处置。
在实践中,通常会削减到显著发生的时间用于垃圾回收了。但是,如果没有存储器的压力,再次,实际收集可能仍然需要较长的时间。

Note: The only thing which is really made deterministic by this is the disposal of scoped objects. In practice this will usually cut the time for garbage collection to occur significantly, too. However, if there's no memory pressure, again, the actual collection could still take a long time.

实施本应该得到的单元测试通过

Implementing this should get the unit test to pass.

请注意:如果根对象绑定 InCallScope 那么这个解决方案不起作用(ninject 3.2.2 / 3.2.0 NamedScope)。我认为这是由于与 InCallScope 但遗憾的是我没有汇报(错误)几年前的一个错误。我可能只是以及被误以为,虽然。

Note: if the root object is bound InCallScope then this solution does not work (ninject 3.2.2 / NamedScope 3.2.0). I think it's due to a bug with InCallScope but sadly i failed to report it (the bug) a few years back. I may just as well be mistaking, though.

public class View : INotifyWhenDisposed
{
    public View(ViewModel viewModel)
    {
        ViewModel = viewModel;
    }

    public event EventHandler Disposed;

    public ViewModel ViewModel { get; private set; }

    public bool IsDisposed { get; private set; }

    public void Dispose()
    {
        if (!this.IsDisposed)
        {
            this.IsDisposed = true;
            var handler = this.Disposed;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }
}

public class ViewModel : IDisposable
{
    public bool IsDisposed { get; private set; }

    public void Dispose()
    {
        this.IsDisposed = true;
    }
}

public class IntegrationTest
{
    private const string ScopeName = "ViewScope";

    [Fact]
    public void Foo()
    {
        var kernel = new StandardKernel();
        kernel.Bind<View>().ToSelf()
            .DefinesNamedScope(ScopeName);
        kernel.Bind<ViewModel>().ToSelf()
            .InNamedScope(ScopeName);

        var view = kernel.Get<View>();

        view.ViewModel.IsDisposed.Should().BeFalse();

        view.Dispose();

        view.ViewModel.IsDisposed.Should().BeTrue();
    }
}



它甚至与 DefineDependency工作 WithCreatorAsConstructorArgument



我没有dotMemory.Unit但这种检查是否ninject保持较强的参考它的缓存中的对象:

It even works with DefineDependency and WithCreatorAsConstructorArgument

I don't have dotMemory.Unit but this checks whether ninject keeps a strong reference to the objects in its cache:

namespace UnitTestProject
{
    using FluentAssertions;
    using Ninject;
    using Ninject.Extensions.DependencyCreation;
    using Ninject.Extensions.NamedScope;
    using Ninject.Infrastructure.Disposal;
    using System;
    using Xunit;

    public class UnitTest1
    {
        [Fact]
        public void TestMethod()
        {
            // Arrange
            var kernel = new StandardKernel();
            const string namedScope = "namedScope";
            kernel.Bind<View>().ToSelf()
                .DefinesNamedScope(namedScope);
            kernel.DefineDependency<View, Presenter>();
            kernel.Bind<ViewModel>().ToSelf().InNamedScope(namedScope);

            Presenter presenterInstance = null;
            kernel.Bind<Presenter>().ToSelf()
                .WithCreatorAsConstructorArgument("view")
                .OnActivation(x => presenterInstance = x);

            var view = kernel.Get<View>();

            // named scope should result in presenter and view getting the same view model instance
            presenterInstance.Should().NotBeNull();
            view.ViewModel.Should().BeSameAs(presenterInstance.ViewModel);

            // disposal of named scope root should clear all strong references which ninject maintains in this scope
            view.Dispose();

            kernel.Release(view.ViewModel).Should().BeFalse();
            kernel.Release(view).Should().BeFalse();
            kernel.Release(presenterInstance).Should().BeFalse();
            kernel.Release(presenterInstance.View).Should().BeFalse();
        }
    }

    public class View : INotifyWhenDisposed
    {
        public View()
        {
        }

        public View(ViewModel viewModel)
        {
            ViewModel = viewModel;
        }

        public event EventHandler Disposed;

        public ViewModel ViewModel { get; private set; }

        public bool IsDisposed { get; private set; }

        public void Dispose()
        {
            if (!this.IsDisposed)
            {
                this.IsDisposed = true;
                var handler = this.Disposed;
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
    }

    public class ViewModel
    {
    }

    public class Presenter
    {
        public View View { get; set; }
        public ViewModel ViewModel { get; set; }

        public Presenter(View view, ViewModel viewModel)
        {
            View = view;
            ViewModel = viewModel;
        }
    }
}

这篇关于NamedScope和垃圾收集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆