2009-04-14 6 views
12

GC.Collectは、バックグラウンドスレッドでガベージコレクションを開始し、すぐに戻ります。 GC.Collectを同期的に実行するにはどうすればよいですか?ガベージコレクションの完了を待ちますか?GC.Collectを同期的に実行します

これはNUnitテストの文脈の中にあります。私はgcConcurrentの設定をテストアセンブリのapp.configファイルに追加しようとしましたが、nunit.exe.configで同じことを試しました。何も影響はありませんでした。デバッグすると、GC.Collect(NUnitの "TestRunnerThread")と呼ばれるスレッドではなく、 "GC Finalizer Thread"でファイナライザが実行されており、両方のスレッドが同時に実行されています。

背景:特定のクラスがリークすると(破棄しないでください)、テストが失敗します。だから私はそのクラスにスタティックwasLeakedフラグを設定するファイナライザを追加しました。私のテストTearDownはGC.Collect()を呼び出し、wasLeakedが真であればスローします。しかし、確定的に失敗するわけではありません。wasLeakedと読むと、通常ファイナライザはまだ呼び出されていないからです。 (ガベージコレクションが最終的に終了した後にそれは、代わりにいくつかの後にテストを失敗した。)

答えて

12

ファイナライザは専用の優先度の高いバックグラウンドスレッドで実行されます。あなたのポストで背景から、私はあなたが単にファイナライズのために任意の非根ざしインスタンスをスケジュールし、ファイナライザスレッドが完了するのを、スレッドが待機する

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

Collect()を行うことができますことを集めます。

+0

パーフェクト!これはまさに私が探していたものです。ありがとう! –

5

あなたは、GC.RegisterForFullGCNotificationを使用GC.Collect(GC.MaxGeneration)し、その後GC.WaitForFullGCCompleteGC.WaitForPendingFinalizers方法で完全なコレクションをトリガーするが、唯一のあなたのテストでこれを使用することを確認することができ、プロダクションコードとして使用しないでください。

+0

ドキュメントでは、WaitForFullGCApproachとWaitForFullGCCompleteは常に一緒に使用する必要があります。 GCを明示的に起動しているときに、GCアプローチを待つ方法はありますか?これを行うコードサンプルがありますか? –

+0

遅れて申し訳ありません。あなたのコードに多かれ少なかれ適切な説明とサンプルがあります。 http://msdn.microsoft.com/en-us/library/cc713687.aspx あなたのアプローチの通知制限を選択すると、基本的にはすぐに通知されます。 – Lucero

2

これを行うより簡単な方法は、mockingを使用して、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(); 
} 
+0

これは、確定的なリリースをテストする優れたアプローチですが、ファイナライザコードが正しく動作するかどうかはテストしません。したがって、Joeは正確にテストしたいものに応じて、両方のアプローチを使用することができます。 – Lucero

+0

私は、彼のクラスのすべてがDispose(3段落)と呼ばれることを確認することを理解しました。 – tvanfosson

+0

Disposeパターンは、保護された(仮想)Dispose(bool disposing)メソッドを使用し、IDIsposableによって呼び出されます。disposing = trueで廃棄し、必要に応じてdisposing = falseで終了します。したがって、Disposeのコードパスがどのようなものかはわかりませんでした。 – Lucero

2

ファイナライザは、同時GCを使用しているかどうかにかかわらず、常に別のスレッドで実行されます。ファイナライザが実行されていることを確認するには、代わりにGC.WaitForPendingFinalizersを試してください。

関連する問題