同步运行GC.Collect

时间:2009-04-14 18:23:53

标签: .net nunit garbage-collection

GC.Collect似乎在后台线程中启动垃圾收集,然后立即返回。如何同步运行GC.Collect - 即等待垃圾收集完成?

这是在NUnit测试的上下文中。我尝试将gcConcurrent设置添加到我的测试程序集的app.config文件中,我尝试使用nunit.exe.config。没有任何影响 - 当我调试时,我仍然可以看到终结器在“GC Finalizer Thread”上运行,而不是调用GC.Collect(NUnit的“TestRunnerThread”)的线程,并且两个线程同时运行

背景:我希望我的测试在他们泄漏(不要调用Dispose on)特定类时失败。所以我在该类中添加了一个终结器,用于设置静态wasLeaked标志;然后我的测试TearDown调用GC.Collect()然后如果wasLeaked为真则抛出。但它并没有确定性地失败,因为当它读取wasLeaked时,终结器通常甚至还没有被调用。 (在垃圾收集最终完成之后,它在一些后来的测试中失败了。)

4 个答案:

答案 0 :(得分:13)

终结器在专用的高优先级后台线程上运行。从你帖子的背景中我收集到你可以简单地做到

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

Collect()将安排任何非根实例进行最终确定,然后线程将等待终结器线程完成。

答案 1 :(得分:5)

您可以使用GC.RegisterForFullGCNotification,使用GC.Collect(GC.MaxGeneration)触发完整集合,然后触发GC.WaitForFullGCCompleteGC.WaitForPendingFinalizers方法,但请确保仅在测试中使用此方法,不应该用于生产代码。

答案 2 :(得分:2)

更简单/更好的方法可能是使用模拟并检查Dispose被明确调用的期望。

使用RhinoMocks的示例

public void SomeMethodTest()
{
     var disposable = MockRepository.GenerateMock<DisposableClass>();

     disposable.Expect( d => d.Dispose() );

     // use constructor injection to pass in mock `DisposableClass` object
     var classUnderTest = new ClassUnderTest( disposable ); 

     classUnderTest.SomeMethod();

     disposable.VerifyAllExpectations();
}

如果该方法需要创建然后处理该对象,那么我将使用并注入一个能够创建模拟对象的工厂类。以下示例使用工厂存根,因为它不是我们在此测试中测试的内容。

public void SomeMethod2Test()
{
     var factory = MockRepository.Stub<DisposableFactory>();
     var disposable = MockRepository.GenerateMock<DisposableClass>();

     factory.Stub( f => f.CreateDisposable() ).Return( disposable );         
     disposable.Expect( d => d.Dispose() );

     // use constructor injection to pass in mock factory
     var classUnderTest = new ClassUnderTest( factory ); 

     classUnderTest.SomeMethod();

     disposable.VerifyAllExpectations();
}

答案 3 :(得分:2)

无论您是否使用并发GC,终结器始终在单独的线程上运行。如果您想确保已经运行终结器,请尝试使用GC.WaitForPendingFinalizers