C# 如何编写单元测试来确定对象是否可以被垃圾收集?

C# 如何编写单元测试来确定对象是否可以被垃圾收集?,c#,.net,unit-testing,garbage-collection,C#,.net,Unit Testing,Garbage Collection,关于,我需要检查将由Castle Windsor实例化的组件是否可以在我的代码使用完后进行垃圾收集。我已经在上一个问题的答案中尝试了这个建议,但它似乎没有像预期的那样起作用,至少对我的代码来说是这样。因此,我想编写一个单元测试,测试在我的一些代码运行后,是否可以对特定的对象实例进行垃圾收集 这有可能以可靠的方式实现吗 编辑 我目前根据Paul Stovell的答案进行了以下测试,测试成功: [TestMethod] public void ReleaseTest() {

关于,我需要检查将由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--;
        }
    }
[TestMethod]
公共无效释放测试()
{
WindsorContainer容器=新WindsorContainer();
container.Kernel.ReleasePolicy=new NoTrackingReleasePolicy();
container.AddComponentWithLifestyle(LifestyleType.Transient);
Assert.AreEqual(0,ReleaseTester.refCount);
var weakRef=新的WeakReference(container.Resolve());
Assert.AreEqual(1,ReleaseTester.refCount);
GC.Collect();
GC.WaitForPendingFinalizers();
Assert.AreEqual(0,ReleaseTester.refCount,“未发布组件”);
}
私有类释放测试器
{
公共静态int refCount=0;
公共发布测试员()
{
refCount++;
}
~ReleaseTester()
{
参考计数--;
}
}

基于上述测试,我是否可以得出结论,温莎在使用NoTrackingReleasePolicy时不会泄漏内存?

也许您可以在测试完成后按住它,然后检查它是否不再活动(即!IsAlive)。

这是我通常做的:

[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);
}

注意:在生产应用程序中,应该调用GC.Collect()的次数非常少。但测试泄漏是一个合适的例子

这不是答案,但是您可能希望尝试在调试发布模式下运行代码(为了比较)。 根据我的经验,Debug版本的JIT'ed代码更容易调试,因此可能会看到引用保持更长时间(我相信函数范围),但是,在Release模式下JIT的代码可能会在对象超出范围和发生收集时,让对象快速准备好收集

也没有回答您的问题::-)
我希望看到您在互操作模式(托管和本机)下使用VisualStudio调试此代码,然后在显示消息框或其他内容后中断。然后您可以打开Debug->Windows Immediate,然后键入

load sos
(Change to thread 0)
!dso
!do <object>
!gcroot <object> (and look for any roots)
加载sos
(更改为线程0)
!dso
!做
!gcroot(并查找任何根)
(或者你可以像其他人在以前的帖子中发布的那样使用Windbg)

谢谢, 基于Aaron,我创建了一个更可重用的断言方法。因为
字符串
是按值复制的,所以我添加了一个显式检查。它们可以由垃圾收集器收集

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 );
}
publicstaticvoid是垃圾收集的(ref-TObject@object)
对象:班级
{
动作emptyAction=o=>{};
IsGarbageCollected(ref@object,emptyAction);
}
公共静态空间垃圾收集(
ref TObject@object,
动作(使用对象)
对象:班级
{
if(typeof(TObject)=typeof(string))
{
//字符串是按值复制的,无论如何都不会泄漏。
返回;
}
int generation=GC.GetGeneration(@object);
useObject(@object);
WeakReference reference=新的WeakReference(@object,true);
@object=null;
//物体现在应该已经超出范围了,
//这样垃圾收集器就可以清理它了。
GC.Collect(生成,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 );
}
[TestMethod]
公共空间垃圾收集测试()
{
//不包含任何引用的空对象。
对象为空=新对象();
已收集资产服务商IsGarbageCollected(参考值为空);
//字符串按值复制,但可收集!
字符串@string=”“;
AssertHelper.IsGarbageCollected(参考@字符串);
//把参考资料放在周围。
object hookedEvent=新对象();
#pragma警告禁用168
对象引用copy=hookedEvent;
#pragma警告恢复168
AssertHelper.ThrowsException.

使用框架(它是免费的)

[TestMethod]
公共无效释放测试()
{
//安排
WindsorContainer容器=新WindsorContainer();
container.Kernel.ReleasePolicy=new NoTrackingReleasePolicy();
container.AddComponentWithLifestyle(LifestyleType.Transient);
var target=container.Resolve()
//表演
target=null;
//断言
dotMemory.Check(内存=>
断言你是平等的(
0, 
memory.GetObjects(其中=>where.Type.Is().ObjectScont,
“未发布的组件”);
}

这只会测试它是否被垃圾收集。@Ray:是的,但过程是强制执行GC,等待终结器,然后检查IsAlive。谢谢。我知道我通常不应该调用GC.Collect,我需要它来测试我怀疑内存泄漏的部分代码。很好!是否可以使用作用域块来代替of匿名代理?@ VIDSTEGE,我认为这不是工作的——在C++中,作用域块是不一样的。一个作用域块的末端不足以允许内存GC,但是你必须尝试它来确定。要添加到关于范围块的位。在发布版本中,它可能并可能不释放块内的强引用。ng取决于实际jitted代码是否重新使用保存引用的堆栈内存或寄存器(因此,如果
[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");
}