Jakie strategie i narzędzia są przydatne do znajdowania wycieków pamięci w .NET?

152

Pisałem C ++ przez 10 lat. Napotkałem problemy z pamięcią, ale można je było naprawić przy rozsądnym wysiłku.

Od kilku lat piszę w C #. Wciąż mam wiele problemów z pamięcią. Są trudne do zdiagnozowania i naprawienia ze względu na brak determinanty, a ponieważ filozofia C # polega na tym, że nie powinieneś się martwić o takie rzeczy, gdy zdecydowanie to robisz.

Jednym ze szczególnych problemów jest to, że muszę jawnie usunąć i wyczyścić wszystko w kodzie. Jeśli tego nie zrobię, to profilery pamięci tak naprawdę nie pomogą, ponieważ jest tak dużo plew, że nie możesz znaleźć wycieku we wszystkich danych, które próbują ci pokazać. Zastanawiam się, czy mam zły pomysł, czy też narzędzie, które mam, nie jest najlepsze.

Jakie strategie i narzędzia są przydatne do rozwiązywania problemów z wyciekami pamięci w .NET?

Scott Langham
źródło
Tytuł twojego posta tak naprawdę nie pasuje do pytania w twoim poście. Proponuję zaktualizować tytuł.
Kevin,
Masz rację. Przepraszam, zaczynałam się trochę znudzić obecnym wyciekiem, na który poluję! Zaktualizowano tytuł.
Scott Langham,
3
@Scott: Nie miej dość .NET, to nie jest problem. Twój kod to.
GEOCHET
3
Tak, mój kod lub biblioteki stron trzecich, z których mam przyjemność korzystać.
Scott Langham,
@Scott: Zobacz moją odpowiedź. MemProfiler jest tego wart. Korzystanie z niego zapewni również zupełnie nowy poziom zrozumienia świata .NET GC.
GEOCHET

Odpowiedzi:

51

Używam MemProfiler firmy Scitech, gdy podejrzewam wyciek pamięci.

Jak dotąd stwierdziłem, że jest bardzo niezawodny i potężny. Przynajmniej raz uratował mi boczek.

GC działa bardzo dobrze w .NET IMO, ale tak jak każdy inny język lub platforma, jeśli napiszesz zły kod, zdarzają się złe rzeczy.

GEOCHET
źródło
3
Tak, spróbowałem tego i pomogło mi dojść do sedna niektórych trudnych wycieków. Okazało się, że największe wycieki spowodowały biblioteki stron trzecich w niezarządzanym kodzie, do których uzyskiwały dostęp przez interop. Byłem pod wrażeniem, że to narzędzie wykryło wycieki w kodzie niezarządzanym, a także w kodzie zarządzanym.
Scott Langham
1
Przyjąłem to jako odpowiedź, ponieważ w końcu to zadziałało, ale myślę, że wszystkie inne odpowiedzi są bardzo przydatne. Nawiasem mówiąc, to narzędzie jest częściej nazywane Mem Profiler SciTech!
Scott Langham
41

W przypadku problemu z zapominaniem o usunięciu wypróbuj rozwiązanie opisane w tym poście na blogu . Oto istota:

    public void Dispose ()
    {
        // Dispose logic here ...

        // It's a bad error if someone forgets to call Dispose,
        // so in Debug builds, we put a finalizer in to detect
        // the error. If Dispose is called, we suppress the
        // finalizer.
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

#if DEBUG
    ~TimedLock()
    {
        // If this finalizer runs, someone somewhere failed to
        // call Dispose, which means we've failed to leave
        // a monitor!
        System.Diagnostics.Debug.Fail("Undisposed lock");
    }
#endif
Jay Bazuzi
źródło
Wolałbym zgłosić wyjątek zamiast Debug.Fail
Pedro77
17

W naszym projekcie wykorzystaliśmy oprogramowanie Ants Profiler Pro firmy Red Gate. Działa bardzo dobrze we wszystkich aplikacjach opartych na języku .NET.

Odkryliśmy, że .NET Garbage Collector jest bardzo „bezpieczny” w czyszczeniu obiektów w pamięci (tak jak powinno). Utrzymywałoby obiekty w pobliżu tylko dlatego, że moglibyśmy go kiedyś używać w przyszłości. Oznaczało to, że musieliśmy bardziej uważać na liczbę obiektów, które napompowaliśmy w pamięci. Ostatecznie przekonwertowaliśmy wszystkie nasze obiekty danych na „inflację na żądanie” (tuż przed żądaniem pola), aby zmniejszyć obciążenie pamięci i zwiększyć wydajność.

EDYCJA: Oto dalsze wyjaśnienie, co mam na myśli, mówiąc o „nadmuchaniu na żądanie”. W naszym modelu obiektowym naszej bazy danych używamy właściwości obiektu nadrzędnego, aby wyeksponować obiekt (y) potomne. Na przykład, gdybyśmy mieli jakiś rekord, który odnosił się do jakiegoś innego „szczegółu” lub „wyszukiwania” na zasadzie jeden do jednego, utworzylibyśmy taką strukturę:

class ParentObject
   Private mRelatedObject as New CRelatedObject
   public Readonly property RelatedObject() as CRelatedObject
      get
         mRelatedObject.getWithID(RelatedObjectID)
         return mRelatedObject
      end get
   end property
End class

Odkryliśmy, że powyższy system powodował pewne problemy z pamięcią i wydajnością, gdy w pamięci było dużo rekordów. Dlatego przeszliśmy na system, w którym obiekty były nadmuchiwane tylko wtedy, gdy były wymagane, a wywołania bazy danych były wykonywane tylko wtedy, gdy było to konieczne:

class ParentObject
   Private mRelatedObject as CRelatedObject
   Public ReadOnly Property RelatedObject() as CRelatedObject
      Get
         If mRelatedObject is Nothing
            mRelatedObject = New CRelatedObject
         End If
         If mRelatedObject.isEmptyObject
            mRelatedObject.getWithID(RelatedObjectID)
         End If
         return mRelatedObject
      end get
   end Property
end class

Okazało się to znacznie bardziej wydajne, ponieważ obiekty były przechowywane poza pamięcią, dopóki nie były potrzebne (uzyskano dostęp do metody Get). Zapewnił bardzo duży wzrost wydajności w ograniczaniu trafień w bazie danych i ogromny przyrost miejsca w pamięci.

znak
źródło
Popieram ten produkt. To był jeden z najlepszych profilerów, z jakich korzystałem.
Gord
Okazało się, że program profilujący jest całkiem dobry do wyszukiwania problemów z wydajnością. Jednak narzędzia do analizy pamięci były dość słabe. Znalazłem wyciek za pomocą tego narzędzia, ale pomogły mi w zidentyfikowaniu przyczyny wycieku. I wcale nie pomaga, jeśli wyciek ma miejsce w niezarządzanym kodzie.
Scott Langham
Ok, nowa wersja 5.1 jest o wiele lepsza. Lepiej jest pomóc ci znaleźć przyczynę wycieku (chociaż - nadal jest z nim kilka problemów, które ANTS powiedział mi, że naprawią w następnej wersji). Nadal jednak nie robi kodu niezarządzanego, ale jeśli nie przejmujesz się kodem niezarządzanym, jest to teraz całkiem dobre narzędzie.
Scott Langham
7

Nadal musisz martwić się o pamięć podczas pisania kodu zarządzanego, chyba że aplikacja jest trywialna. Zasugeruję dwie rzeczy: najpierw przeczytaj CLR przez C #, ponieważ pomoże ci to zrozumieć zarządzanie pamięcią w .NET. Po drugie, naucz się korzystać z narzędzia takiego jak CLRProfiler (Microsoft). To może dać ci wyobrażenie o tym, co powoduje wyciek pamięci (np. Możesz przyjrzeć się fragmentacji sterty dużych obiektów)

Zac Gochenour
źródło
Tak. CLRPRofiler jest całkiem fajny. Może być nieco wybuchowy, gdy próbuje się przekopać widok przydzielonych obiektów, ale wszystko tam jest. To zdecydowanie dobry punkt wyjścia, zwłaszcza że jest bezpłatny.
Scott Langham
6

Czy używasz niezarządzanego kodu? Jeśli nie używasz kodu niezarządzanego, według Microsoft przecieki pamięci w tradycyjnym sensie nie są możliwe.

Pamięć używana przez aplikację może jednak nie zostać zwolniona, więc alokacja pamięci aplikacji może rosnąć w trakcie jej życia.

Od Jak zidentyfikować wycieki pamięci w środowisku uruchomieniowym języka wspólnego w witrynie Microsoft.com

W aplikacji .NET Framework może wystąpić przeciek pamięci, jeśli jako część aplikacji używasz niezarządzanego kodu. Ten niezarządzany kod może przeciekać pamięć, a środowisko wykonawcze .NET Framework nie może rozwiązać tego problemu.

Ponadto projekt może wydawać się mieć tylko przeciek pamięci. Ten stan może wystąpić, jeśli zadeklarowano wiele dużych obiektów (takich jak obiekty DataTable), a następnie dodano je do kolekcji (na przykład DataSet). Zasoby, które należą do tych obiektów, mogą nigdy nie zostać zwolnione, a zasoby pozostaną żywe przez cały czas wykonywania programu. Wydaje się, że jest to przeciek, ale w rzeczywistości jest to tylko objaw alokacji pamięci w programie.

Aby poradzić sobie z tego typu problemami, możesz zaimplementować IDisposable . Jeśli chcesz zobaczyć niektóre strategie radzenia sobie z zarządzaniem pamięcią, sugerowałbym wyszukanie IDisposable, XNA, zarządzanie pamięcią, ponieważ twórcy gier muszą mieć bardziej przewidywalne zbieranie śmieci, a więc muszą zmusić GC do zrobienia tego.

Częstym błędem jest nieusuwanie programów obsługi zdarzeń, które subskrybują obiekt. Subskrypcja programu obsługi zdarzeń uniemożliwi ponowne odtworzenie obiektu. Przyjrzyj się również instrukcji using , która umożliwia utworzenie ograniczonego zakresu czasu życia zasobu.

Timothy Lee Russell
źródło
5
Zobacz blogs.msdn.com/tess/archive/2006/01/23/… . Nie ma znaczenia, czy wyciek pamięci jest „tradycyjny”, czy nie, nadal jest to wyciek.
Constantin,
2
Rozumiem twój punkt widzenia - ale nieefektywna alokacja i ponowne wykorzystanie pamięci przez program to coś innego niż wyciek pamięci.
Timothy Lee Russell
dobra odpowiedź, dziękuję za przypomnienie mi, że programy obsługi zdarzeń mogą być niebezpieczne.
frameworkninja
3
@Timothy Lee Russel: Jeśli nieograniczona (1) ilość pamięci może pozostać jednocześnie przydzielona (zrootowana) po tym, jak stanie się bezużyteczna (2), bez niczego w systemie, który ma informacje i bodźce niezbędne do unrootowania go w odpowiednim czasie, jest to wyciek pamięci . Nawet jeśli pewnego dnia pamięć zostanie uwolniona, jeśli zgromadzi się wystarczająco dużo bezużytecznych rzeczy, aby zdławić system, zanim to się stanie, jest to wyciek. (1) Większe niż O (N), gdzie N jest kwotą użytecznej alokacji; (2) Rzeczy są bezużyteczne, jeśli usunięcie odniesień do nich nie wpłynęłoby na funkcjonalność programu.
supercat
2
@Timothy Lee Russel: Normalny wzorzec „wycieku pamięci” występuje, gdy pamięć jest przechowywana przez jedną jednostkę w imieniu innej istoty , spodziewając się, że zostanie ona poinformowana, gdy nie jest już potrzebna, ale ta ostatnia porzuca jednostkę bez informowania o tym pierwszej. Istota przechowująca pamięć tak naprawdę jej nie potrzebuje, ale nie ma sposobu, aby to ustalić.
supercat
5

Ten blog zawiera naprawdę wspaniałe instrukcje korzystania z windbg i innych narzędzi do śledzenia wszelkiego rodzaju wycieków pamięci. Doskonała lektura, aby rozwinąć swoje umiejętności.

twk
źródło
5

Właśnie miałem wyciek pamięci w usłudze systemu Windows, który naprawiłem.

Najpierw wypróbowałem MemProfiler . Uważam, że jest naprawdę trudny w użyciu i wcale nie jest przyjazny dla użytkownika.

Następnie użyłem JustTrace, który jest łatwiejszy w użyciu i daje więcej szczegółów na temat obiektów, które nie są prawidłowo usuwane.

Pozwoliło mi to naprawdę łatwo rozwiązać problem wycieku pamięci.

billybob
źródło
3

Jeżeli przecieki obserwujesz są ze względu na uciekający realizacji pamięci podręcznej, to jest scenariusz, w którym może warto rozważyć użycie WeakReference. Może to pomóc w zapewnieniu zwolnienia pamięci, gdy jest to konieczne.

Jednak w IMHO lepiej byłoby rozważyć indywidualne rozwiązanie - tylko Ty naprawdę wiesz, jak długo musisz przechowywać obiekty w pobliżu, więc zaprojektowanie odpowiedniego kodu porządkowego dla Twojej sytuacji jest zwykle najlepszym podejściem.

Chris Ballard
źródło
3

Wolę dotmemory od Jetbrains

josepainumkal
źródło
możesz być jedyny :)
HellBaby
Ja też tego próbowałem. Myślę, że to dobre narzędzie. Łatwy w użyciu, pouczający. Integruje się z programem Visual Studio
redeye
W naszym przypadku podczas rozwiązywania problemów z wyciekami pamięci narzędzie Visual Studio Snapshot uległo awarii / nie wykonało migawki. Dotmemory zachowywał chłód i obsługiwał wiele migawek o pojemności 3+ GB z (pozornie) łatwością.
Michael Kargl,
3

Big guns - narzędzia do debugowania dla systemu Windows

To niesamowita kolekcja narzędzi. Za jego pomocą można analizować zarówno zarządzane, jak i niezarządzane sterty i można to robić w trybie offline. Było to bardzo przydatne w przypadku debugowania jednej z naszych aplikacji ASP.NET, która była odtwarzana z powodu nadmiernego wykorzystania pamięci. Musiałem tylko stworzyć pełny zrzut pamięci żywego procesu działającego na serwerze produkcyjnym, cała analiza została wykonana offline w WinDbg. (Okazało się, że jakiś programista nadużywał pamięci masowej sesji w pamięci).

Blog „Jeśli zepsuty to…” zawiera bardzo przydatne artykuły na ten temat.

Constantin
źródło
2

Najlepszą rzeczą, o której należy pamiętać, jest śledzenie odniesień do obiektów. Bardzo łatwo jest skończyć z wiszącymi odnośnikami do obiektów, na których już Ci nie zależy. Jeśli nie zamierzasz już czegoś używać, pozbądź się tego.

Przyzwyczaj się do korzystania z dostawcy pamięci podręcznej z ruchomymi okresami ważności, więc jeśli coś nie jest przywoływane w żądanym oknie czasowym, zostanie wyłuskane i wyczyszczone. Ale jeśli jest często używany, powie to w pamięci.

Gord
źródło
2

Jednym z najlepszych narzędzi jest użycie narzędzi debugowania dla systemu Windows i wykonanie zrzutu pamięci procesu za pomocą adplus , a następnie użycie windbg i wtyczki sos do analizy pamięci procesu, wątków i stosów wywołań.

Możesz użyć tej metody do identyfikowania problemów na serwerach, po zainstalowaniu narzędzi, udostępnij katalog, a następnie połącz się z udziałem z serwera za pomocą (użycie sieci) i albo zawieszaj się, albo zawieszaj zrzut procesu.

Następnie przeanalizuj offline.

Stuart McConnell
źródło
Tak, działa to dobrze, szczególnie w przypadku bardziej zaawansowanych rzeczy lub diagnozowania problemów w wydanym oprogramowaniu, do którego nie można łatwo dołączyć debugera. Ten blog zawiera wiele wskazówek, jak dobrze korzystać z tych narzędzi: blogs.msdn.com/tess
Scott Langham
2

Po jednej z moich poprawek dla zarządzanej aplikacji miałem to samo, na przykład jak sprawdzić, czy moja aplikacja nie będzie miała takiego samego wycieku pamięci po mojej następnej zmianie, więc napisałem coś w rodzaju struktury weryfikacji wydania obiektu, spójrz na pakiet Nuget ObjectReleaseVerification . Próbkę można znaleźć tutaj https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample oraz informacje o tym przykładzie http://outcoldman.ru/en/blog/show/322

outcoldman
źródło
0

W programie Visual Studio 2015 rozważ użycie gotowego narzędzia diagnostycznego użycia pamięci do zbierania i analizowania danych o użyciu pamięci.

Narzędzie Użycie pamięci umożliwia wykonanie jednej lub więcej migawek zarządzanej i natywnej sterty pamięci w celu zrozumienia wpływu użycia pamięci na typy obiektów.

Michael Freidgeim
źródło
0

jedno z najlepszych narzędzi, z których korzystałem, DotMemory. możesz użyć tego narzędzia jako rozszerzenia w VS. po uruchomieniu aplikacji możesz przeanalizować każdą część pamięci (według obiektu, NameSpace itp.), z której korzysta Twoja aplikacja i zrobić migawkę tego , Porównaj to z innymi SnapShotami. DotMemory

Rebwar
źródło