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