2011-01-19 11 views
10

Istnieje wiele pytań, w których można znaleźć wykrywanie wycieku obiektów IDisposable. Wygląda na to, że odpowiedź brzmi: "you can't".Wykrywanie "wyciekanych" obiektów IDisposable

Właśnie sprawdziłem z najbardziej trywialnym przypadkiem testowym, że FxCop 10.0 tego nie robi, ReSharper 4 z MSVS2010 tego nie robi.

Wydaje mi się to niewłaściwe, gorsze niż wycieki pamięci w C (dla których przynajmniej stworzyliśmy narzędzia do wykrywania).

Myślałem: Czy to możliwe, używając refleksji i innych niejasnych zaawansowanych technik, które mogę wprowadzić w runtime, w finalizatorze, aby sprawdzić, czy został wywołany Dispose?

Co powiecie na magiczne sztuczki z WinDBG + SOS?

Nawet jeśli nie ma na to narzędzi, chciałbym się dowiedzieć, czy jest to teoretycznie możliwe (moje C# nie jest zbyt ostre).

Pomysły?

UWAGA Tytuł tego pytania mógł być mylący. Prawdziwym pytaniem tutaj powinno być , czy obiekt IDisposable został odpowiednio Disposed(). Pozbycie się przez GC nie liczy się, ponieważ uważam to za błąd.

Edytuj: Rozwiązanie: .NET Profiler Memory wykonuje zadanie. Po zakończeniu programu musimy po prostu spisać kilka GC.Collect(), aby nasz profiler mógł poprawnie pobrać statystyki.

+0

Powodem, dla którego istnieją narzędzia dla C++, ale może nie dla C# jest to, że zasoby w C# są zasadniczo różne, ponieważ zasoby niezarządzane * nie są już powiązane z czasem życia obiektu *. Co można prześledzić, zarówno w języku C#, jak i C++, to czas życia obiektu i to, czy obiekt został właściwie usunięty. Jednak zasoby jednorazowe w języku C# nie są w żaden sposób związane z obiektem życia, co powoduje, że śledzenie ich jest znacznie trudniejsze. Aby porównać, spróbuj śledzić wyciek zasobów GDI, które nie są powiązane przez RAII do czasu życia obiektu w C++. Nie tak łatwo. –

+0

Trochę się nad tym zastanawiam. Rozwinąłem nawyk szybkiego sprawdzania typów podczas pisania kodu, aby sprawdzić, czy odziedziczą po 'IDisposable'. Jeśli to zrobię, zawijam je w 'użyciu' w zakresie, w jakim muszą żyć. Nie działa on dla istniejącego kodu, ale pomyślałem, że o tym wspomnę. –

+0

Zobacz ten wpis, w którym można użyć analizy kodu programu Visual Studio w celu wykrycia problemów z iDisposable w czasie kompilacji: http://stackoverflow.com/a/6213977/2862 –

Odpowiedz

11

Nie szukałeś wystarczająco mocno. Dostępnych jest wiele Profili pamięci .NET, które będą przeglądać twój program podczas pracy i poinformować, gdzie i jak używana jest twoja pamięć (i co to jest wyciek).

chciałbym sprawdzić jedną z następujących czynności:

Microsoft's CLR Memory Profiler (free)
RedGate ANTS Memory Profiler
JetBrain's DotTrace (includes code profiler as well)
SciTech .NET Memory Profiler

aktualizacji

SCITECH w .NET Memory Profiler ma funkcję o nazwie „Dispose Tracker "to pasuje do rachunku dla PO żądanie śledzenia tylko wywołań usuwania w ich aplikacji.

+0

Czy wykryje to "Dispose" lub po prostu pamięć? Co się stanie, jeśli mój obiekt 'IDisposable' ma po prostu' Console.WriteLine ("baz"); '(słaby przykład, wiem, ale masz rację) i chcę się upewnić, że rzeczywiście jest on nazywany NIE przez GC? – kizzx2

+0

@ kizzx2 - wykryje wszystko, ale możesz go zawęzić, aby znaleźć to, czego szukasz. –

+0

Miałem najlepsze doświadczenia z ANTS Memory Profiler RedGate. –

3

możesz to zrobić, dodając Finalizer do obiektów IDisposable. W finalizatorze można sprawdzić, czy obiekt został usunięty, czy nie. Jeśli nie została usunięta, możesz to potwierdzić lub napisać coś do dziennika, lub cokolwiek innego.

~Disposable() 
{ 
#if DEBUG 
      // In debug-builds, make sure that a warning is displayed when the Disposable object hasn't been 
      // disposed by the programmer. 

      if(_disposed == false) 
      { 
       System.Diagnostics.Debug.Fail ("There is a disposable object which hasn't been disposed before the finalizer call: {0}".FormatString (this.GetType().Name)); 
      } 
#endif 
      Dispose (false); 
} 

Można czynnik tej funkcji w klasie bazowej - Disposable - na przykład, który może być używany jako szablon do wdrożenia Disposable wzór na przykład.

Podoba Ci się to, na przykład:

/// <summary> 
    /// Abstract base class for Disposable types.  
    /// </summary> 
    /// <remarks>This class makes it easy to correctly implement the Disposable pattern, so if you have a class which should 
    /// be IDisposable, you can inherit from this class and implement the DisposeManagedResources and the 
    /// DisposeUnmanagedResources (if necessary). 
    /// </remarks> 
    public abstract class Disposable : IDisposable 
    { 
     private bool     _disposed = false; 

     /// <summary> 
     /// Releases the managed and unmanaged resources. 
     /// </summary> 
     public void Dispose() 
     { 
      Dispose (true); 
      GC.SuppressFinalize (this); 
     } 

     /// <summary> 
     /// Releases the unmanaged and managed resources. 
     /// </summary> 
     /// <param name="disposing">When disposing is true, the managed and unmanaged resources are 
     /// released. 
     /// When disposing is false, only the unmanaged resources are released.</param> 
     [System.Diagnostics.CodeAnalysis.SuppressMessage ("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")] 
     protected void Dispose(bool disposing) 
     { 
      // We can suppress the CA1063 Message on this method, since we do not want that this method is 
      // virtual. 
      // Users of this class should override DisposeManagedResources and DisposeUnmanagedResources. 
      // By doing so, the Disposable pattern is also implemented correctly. 

      if(_disposed == false) 
      { 
       if(disposing) 
       { 
        DisposeManagedResources(); 
       } 
       DisposeUnmanagedResources(); 

       _disposed = true; 
      } 
     } 

     /// <summary> 
     /// Override this method and implement functionality to dispose the 
     /// managed resources. 
     /// </summary> 
     protected abstract void DisposeManagedResources(); 

     /// <summary> 
     /// Override this method if you have to dispose Unmanaged resources. 
     /// </summary> 
     protected virtual void DisposeUnmanagedResources() 
     { 
     } 

     /// <summary> 
     /// Releases unmanaged resources and performs other cleanup operations before the 
     /// <see cref="Disposable"/> is reclaimed by garbage collection. 
     /// </summary> 
     [System.Diagnostics.CodeAnalysis.SuppressMessage ("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")] 
     ~Disposable() 
     { 
#if DEBUG 
      // In debug-builds, make sure that a warning is displayed when the Disposable object hasn't been 
      // disposed by the programmer. 

      if(_disposed == false) 
      { 
       System.Diagnostics.Debug.Fail ("There is a disposable object which hasn't been disposed before the finalizer call: {0}".FormatString (this.GetType().Name)); 
      } 
#endif 
      Dispose (false); 
     } 
    } 
+0

To właśnie Raymond na starej nowej rzeczy [zasugerował] (http://blogs.msdn.com/b/oldnewthing/archive/2010/08/13/10049634.aspx). Używam go cały czas do własnych zajęć, teraz myślałem, czy mogę też złapać np. 'FileStream'. – kizzx2

+0

jest to podejście ubogie, ponieważ nie można go konserwować i dodaje zbyt wiele bałaganu i złożoności. Zamiast tego użyj profilera. Cały czas używam programu Red Gate Memory Profiler. –

+0

Dlaczego to jest kiepskie podejście? Dlaczego nie jest to możliwe do utrzymania i dodaje jej złożoności? Jeśli na przykład czynnik ten zostanie zaklasyfikowany do klasy bazowej, która na przykład nazywa się "Jednorazowe", nie widzę problemu. Oprócz tego możesz użyć tej klasy bazowej jako "szablonu", aby poprawnie zaimplementować "wzór jednorazowy". –

1

Choć zalecenie @Justin Niessner za prace, uważam, że za pomocą pełnowartościowy profilera zbyt ciężkie.

Stworzyłem moje domowe rozwiązanie: EyeDisposable. Instrumentuje zespoły, aby wykryć, kiedy nie zostały wywołane, gdy Dispose.

+0

Dzięki, wygląda całkiem fajnie! Bardzo by mi się jednak podobało * statyczne * analizy tego (być może jako wtyczka R #). Jeden mały komentarz, w sekcji * Cloning * readme należy uruchomić 'git submodule init' przed' git submodule update'. –

+1

@OhadSchneider Dzięki za head up - analiza statyczna byłaby fajna, ale wiąże się z wieloma fałszywymi trafieniami w nietrywialnych przypadkach - i jest naprawdę o wiele bardziej skomplikowana niż pierwotny zakres tego _small_ narzędzia: P – kizzx2

Powiązane problemy