代码之家  ›  专栏  ›  技术社区  ›  Cameron MacFarland

在.NET中检查垃圾回收吗?[复制]

  •  8
  • Cameron MacFarland  · 技术社区  · 17 年前

    关于 my previous question ,我需要检查一个将由Castle Windsor实例化的组件在我的代码完成使用后是否可以被垃圾回收。我已经在前一个问题的答案中尝试了这个建议,但它似乎并不像预期的那样有效,至少对我的代码来说是这样。所以我想写一个单元测试,测试在我的一些代码运行后,特定对象实例是否可以被垃圾回收。

    有可能以可靠的方式做到这一点吗?

    编辑

    我目前根据Paul Stovell的答案进行了以下测试,测试成功:

         [TestMethod]
        public void ReleaseTest()
        {
            WindsorContainer container = new WindsorContainer();
            container.Kernel.ReleasePolicy = new NoTrackingReleasePolicy();
            container.AddComponentWithLifestyle<ReleaseTester>(LifestyleType.Transient);
            Assert.AreEqual(0, ReleaseTester.refCount);
            var weakRef = new WeakReference(container.Resolve<ReleaseTester>());
            Assert.AreEqual(1, ReleaseTester.refCount);
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Assert.AreEqual(0, ReleaseTester.refCount, "Component not released");
        }
    
        private class ReleaseTester
        {
            public static int refCount = 0;
    
            public ReleaseTester()
            {
                refCount++;
            }
    
            ~ReleaseTester()
            {
                refCount--;
            }
        }
    

    基于上述测试,我可以得出结论,在使用notrackingrelease策略时,Windsor不会泄漏内存,这是对的吗?

    0 回复  |  直到 9 年前
        1
  •  86
  •   Paul Stovell    15 年前

    我通常是这样做的:

    [Test]
    public void MyTest() 
    {
        WeakReference reference;
        new Action(() => 
        {
            var service = new Service();
            // Do things with service that might cause a memory leak...
    
            reference = new WeakReference(service, true);
        })();
    
        // Service should have gone out of scope about now, 
        // so the garbage collector can clean it up
        GC.Collect();
        GC.WaitForPendingFinalizers();
    
        Assert.IsNull(reference.Target);
    }
    

    NB:你应该打电话的时间很少GC.收集()在生产应用程序中。但测试泄漏是一个合适的例子。

        2
  •  4
  •   JacquesB    17 年前

    也许你可以 WeakReference 然后检查它是否已经不活了(即!在测试完成后。

        3
  •  3
  •   Community Mohan Dere    9 年前

    on Paul's answer ,我创建了一个更可重用的Assert方法。因为 string 的值被复制,我为它们添加了一个显式检查。它们可以由垃圾收集器收集。

    public static void IsGarbageCollected<TObject>( ref TObject @object )
        where TObject : class
    {
        Action<TObject> emptyAction = o => { };
        IsGarbageCollected( ref @object, emptyAction );
    }
    
    public static void IsGarbageCollected<TObject>(
        ref TObject @object,
        Action<TObject> useObject )
        where TObject : class
    {
        if ( typeof( TObject ) == typeof( string ) )
        {
            // Strings are copied by value, and don't leak anyhow.
            return;
        }
    
        int generation = GC.GetGeneration( @object );
        useObject( @object );
        WeakReference reference = new WeakReference( @object, true );
        @object = null;
    
        // The object should have gone out of scope about now, 
        // so the garbage collector can clean it up.
        GC.Collect( generation, GCCollectionMode.Forced );
        GC.WaitForPendingFinalizers();
    
        Assert.IsNull( reference.Target );
    }
    

    下面的单元测试显示该函数在一些常见的场景中工作。

    [TestMethod]
    public void IsGarbageCollectedTest()
    {
        // Empty object without any references which are held.
        object empty = new object();
        AssertHelper.IsGarbageCollected( ref empty );
    
        // Strings are copied by value, but are collectable!
        string @string = "";
        AssertHelper.IsGarbageCollected( ref @string );
    
        // Keep reference around.
        object hookedEvent = new object();
        #pragma warning disable 168
        object referenceCopy = hookedEvent;
        #pragma warning restore 168
        AssertHelper.ThrowsException<AssertFailedException>(
            () => AssertHelper.IsGarbageCollected( ref hookedEvent ) );
        GC.KeepAlive( referenceCopy );
    
        // Still attached as event.
        Publisher publisher = new Publisher();
        Subscriber subscriber = new Subscriber( publisher );
        AssertHelper.ThrowsException<AssertFailedException>(
            () => AssertHelper.IsGarbageCollected( ref subscriber ) );
        GC.KeepAlive( publisher );
    }
    

    由于在使用 Release 配置(我假设是编译器优化),如果 GC.KeepAlive() 不会被叫来的。

    can be found in my library .

        4
  •  3
  •   Ed Pavlov    11 年前

    使用 dotMemory Unit 框架(免费)

    [TestMethod]
    public void ReleaseTest()
    {
        // arrange
        WindsorContainer container = new WindsorContainer();
        container.Kernel.ReleasePolicy = new NoTrackingReleasePolicy();
        container.AddComponentWithLifestyle<ReleaseTester>(LifestyleType.Transient);
        var target = container.Resolve<ReleaseTester>()
    
        // act
        target = null;
    
        // assert        
        dotMemory.Check(memory =>
          Assert.AreEqual(
            0, 
            memory.GetObjects(where => where.Type.Is<ReleaseTester>().ObjectsCount, 
            "Component not released");
    }
    
        5
  •  1
  •   AaronBa    17 年前

    这不是一个答案,但是您可能需要尝试在这两种情况下运行代码 调试 和 释放 模式(为了比较)。

    调试 JIT'ed代码的版本更容易调试,因此可能会看到引用保持更长时间(我相信函数作用域),但是,代码JIT 模式一旦超出范围并且发生了收集,它可能会让对象快速准备好进行收集。

    也不回答你的问题:-)
    我很有兴趣看到您使用visualstudio以互操作模式(托管和本机)调试此代码,然后在显示消息框或其他内容后中断。然后,可以打开“调试”>“Windows立即”,然后键入

    load sos
    (Change to thread 0)
    !dso
    !do <object>
    !gcroot <object> (and look for any roots)
    

    (或者你可以使用Windbg,就像其他人在以前的帖子中所说的那样)

    谢谢,