2009-04-14 9 views
12

GC.Collect pojawia się, aby rozpocząć odśmiecanie w wątku tła, a następnie natychmiast powrócić. Jak mogę synchronicznie uruchomić GC.Collect - czyli czekać na zakończenie czyszczenia pamięci?Uruchom GC.Collect synchronicznie

Jest to w kontekście testów NUnit. Próbowałem dodać ustawienie gcConcurrent do pliku app.config mojego zestawu testowego i próbowałem tego samego z plikiem nunit.exe.config. Żaden z nich nie przyniósł żadnego efektu - podczas debugowania nadal widzę, jak finalizator działa na "wątku GC Finalizer", a nie wątku o nazwie GC.Collect ("TestRunnerThread" NUnita), a oba wątki działają jednocześnie.

Tło: Chcę, aby moje testy zakończyły się niepowodzeniem, jeśli wyciekną (nie wywołuj Dispose) określonej klasy. Dlatego dodałem finalizator do tej klasy, która ustawia statyczną flagę: wasLeaked; następnie mój test TearDown wywołuje GC.Collect(), a następnie wyrzuca, jeśli wasLeaked jest prawdziwy. Nie determinuje to jednak deterministycznie, ponieważ kiedy czyta się wasLeaked, finalizator zwykle jeszcze nie został jeszcze wywołany. (To nie późniejszym testu zamiast po zbieranie śmieci wreszcie zakończy.)

Odpowiedz

12

Finalizery są uruchamiane na dedykowanym wątku tła o wysokim priorytecie. Od tła w swoim poście, wnoszę, że można po prostu zrobić

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

Collect() zaplanuje wszelkie nie zakorzenione instancje dla finalizacji i wtedy wątek będzie czekać na gwint finalizator aby zakończyć.

+0

Idealny! Właśnie tego szukałem. Dzięki! –

5

Można użyć GC.RegisterForFullGCNotification, wyzwolić pełną kolekcję GC.Collect(GC.MaxGeneration) a następnie metodami GC.WaitForFullGCComplete i GC.WaitForPendingFinalizers, ale upewnij się, aby skorzystać z tej tylko w swoich badaniach, nie powinny być używane do kodu produkcyjnego.

+0

Dokumenty mówią, że WaitForFullGCApproach i WaitForFullGCComplete powinny być zawsze używane razem. Jak mogę poczekać na podejście GC, gdy wyraźnie wypalam GC? Czy masz próbkę kodu, która to robi? –

+0

Przepraszamy za spóźnioną odpowiedź. Tutaj znajdziesz dobre wyjaśnienie i przykład, które powinny w mniejszym lub większym stopniu dotyczyć Twojego kodu: http://msdn.microsoft.com/en-us/library/cc713687.aspx Możesz wybrać swój limit powiadomień o podejściu, aby od razu otrzymujesz powiadomienie. – Lucero

2

Łatwiejszym/lepszym sposobem na to może być użycie kpiny i sprawdzenie, czy wywołanie zostało jawnie określone.

przykład stosując 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(); 
} 

Jeśli sposób wymaga, aby utworzyć i wyrzucać przedmiotu, wówczas użyć i wstrzyknąć klasy fabrycznej, która jest zdolna do tworzenia mock obiektu. Przykład poniżej używa kodu pośredniczącego w fabryce, ponieważ nie jest to testowany w tym teście.

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

To jest przyjemne podejście do testowania deterministycznego wydania, ale nie sprawdza, czy kod finalizera działa poprawnie. Dlatego Joe może chcieć zastosować oba podejścia, w zależności od tego, co dokładnie chce przetestować. – Lucero

+0

Zrozumiałem, że należy upewnić się, że wszystkie jego klasy nazywają się Dispose (3 paragraph). – tvanfosson

+0

Model Dispose używa metody chronionej (wirtualnej) Dispose (bool disposing) i wywoływanej przez IDIsposable.Pozbywaj się z disposing = true i przez finalizatora (jeśli jest to wymagane) z disposing = false. Dlatego nie byłem pewien, co oznaczała ścieżka do usunięcia. – Lucero

2

Finalizery zawsze działają w oddzielnym wątku, niezależnie od tego, czy korzystasz z współbieżnego GC, czy nie. Jeśli chcesz się upewnić, że finalizatory zostały uruchomione, spróbuj zamiast tego GC.WaitForPendingFinalizers.

Powiązane problemy