Rozwiązywanie problemów z brakiem pamięci (System.OutOfMemoryException) w ASP.NET

Ten artykuł ułatwia rozwiązywanie problemów z błędami braku pamięci w ASP.NET.

Oryginalna wersja produktu: ASP.NET
Oryginalny numer KB: 2020006

Symptomy

Jednym z najczęstszych problemów, które widzimy w usługach pomocy technicznej firmy Microsoft, są OutOfMemoryException scenariusze. Dlatego zebraliśmy kolekcję zasobów, aby pomóc w rozwiązywaniu problemów i identyfikowaniu przyczyn problemów z pamięcią.

Przed omówieniem szczegółów rozwiązywania problemów z elementem OutOfMemoryExceptionnależy zrozumieć, co powoduje ten problem. W przeciwieństwie do tego, co wielu deweloperów uważa, ilość zainstalowanej pamięci RAM nie ma wpływu OutOfMemoryExceptionna możliwość . 32-bitowy system operacyjny może zająć 4 GB wirtualnej przestrzeni adresowej, niezależnie od ilości pamięci fizycznej zainstalowanej w polu. Z tego powodu 2 GB jest zarezerwowane dla systemu operacyjnego (pamięć w trybie jądra), a 2 GB jest przydzielane do procesów w trybie użytkownika. 2 GB przydzielone do pamięci w trybie jądra jest współużytkowane we wszystkich procesach, ale każdy proces otrzymuje własne 2 GB przestrzeni adresowej w trybie użytkownika. Przyjęto założenie, że nie jest uruchomiony przełącznik z włączonym przełącznikiem /3gb .

Gdy aplikacja musi używać pamięci, rezerwuje fragment wirtualnej przestrzeni adresowej, a następnie zatwierdza pamięć z tego fragmentu. Jest to dokładnie to, co .NET Framework modułu odśmiecania pamięci (GC) robi, gdy potrzebuje pamięci, aby zwiększyć zarządzane sterty. Gdy usługa GC potrzebuje nowego segmentu dla sterty małych obiektów (gdzie znajdują się obiekty mniejsze niż 85 KB), przydziela 64 MB. Gdy potrzebuje nowego segmentu dla sterty dużych obiektów, przydziela 16 MB. Te duże alokacje muszą być spełnione z ciągłych bloków 2 GB przestrzeni adresowej, z którą proces musi pracować. Jeśli system operacyjny nie jest w stanie spełnić żądania GC dla ciągłego bloku pamięci, System.OutOfMemoryException występuje (OOM).

Uwaga

32-bitowy proces działający w 64-bitowym systemie operacyjnym może zająć się 4 GB pamięci w trybie użytkownika, a 64-bitowy proces uruchomiony w 64-bitowym systemie operacyjnym może zająć się 8 TB pamięci w trybie użytkownika, więc OOM w 64-bitowym systemie operacyjnym nie jest prawdopodobny. Istnieje możliwość wystąpienia OOM w procesie 32-bitowym działającym w 64-bitowym systemie operacyjnym, ale zwykle nie występuje, dopóki proces nie użyje blisko 3 GB bajtów prywatnych.

Istnieją dwa powody, dla których może wystąpić warunek OOM.

  1. Proces używa dużej ilości pamięci (zazwyczaj ponad 800 MB w środowisku 32-bitowym).
  2. Wirtualna przestrzeń adresowa jest podzielona na fragmenty, co zmniejsza prawdopodobieństwo, że duża, ciągła alokacja zakończy się pomyślnie.

Istnieje również możliwość wyświetlenia warunku OOM ze względu na kombinację 1 i 2. Aby uzyskać więcej informacji, zobacz Troubleshooting System.OutOfMemoryExceptions in ASP.NET (Rozwiązywanie problemów z błędami System.OutOfMemoryExceptions w ASP.NET).

W przypadku wystąpienia OOM można zauważyć co najmniej jeden z następujących objawów:

Jeśli chodzi o określenie przyczyny stanu OOM, pracujesz nad ustaleniem przyczyny wystąpienia dużej ilości pamięci lub fragmentacji przestrzeni adresowej. Chociaż nie możemy udokumentować wszystkich możliwych przyczyn takich sytuacji, istnieją pewne typowe przyczyny, które regularnie widzimy.

Poniższe informacje przedstawiają typowe przyczyny warunków OOM i rozwiązania dotyczące rozwiązywania każdej z tych przyczyn.

Ciągów

Ciągi w aplikacji zarządzanej (aplikacja napisana przy użyciu .NET Framework) są niezmienne. Gdy nowa wartość jest przypisana do ciągu, kopia jest wykonana z istniejącego ciągu. Nowa wartość jest przypisywana do nowego ciągu. Zazwyczaj nie powoduje żadnych problemów. Jednak po połączeniu dużej liczby ciągów powoduje to o wiele więcej alokacji ciągów, niż może zrealizować deweloper. Może to prowadzić do wzrostu pamięci i warunków OOM.

Aby uniknąć OOM z powodu łączenia ciągów, upewnij się, że używasz StringBuilder klasy . Aby uzyskać więcej informacji, zobacz Jak poprawić wydajność łączenia ciągów w języku Visual C#.

Fragmentacja w zarządzanym stosie

Moduł odśmiecania pamięci (GC) w aplikacji zarządzanej kompakuje sterty, aby zmniejszyć ilość fragmentacji. Można jednak przypiąć obiekty w aplikacji zarządzanej. Przypiętych obiektów nie można przenosić podczas kompaktowania sterty. Spowoduje to zmianę adresu, pod którym znajduje się obiekt. Jeśli aplikacja przypina dużą liczbę obiektów i/lub przypina obiekty przez długi czas, może to spowodować fragmentację w zarządzanym stercie. Może to prowadzić do tego, że GC częściej zwiększa zarządzaną stertę i powoduje stan OOM.

Pracowaliśmy nad minimalizowaniem warunków OOM z powodu przypinania od .NET Framework 1.0. W każdej wersji wprowadzono ulepszenia przyrostowe. Jednak nadal istnieją wzorce projektowe, które można zaimplementować, które będą korzystne, jeśli trzeba przypiąć obiekty.

Fragmentacja w przestrzeni adresów wirtualnych

Każdy proces ma przydzieloną pewną ilość pamięci, a ta pamięć reprezentuje przestrzeń va dla procesu. Jeśli obszar va zostanie podzielony na fragmenty, zwiększa to prawdopodobieństwo, że GC nie może uzyskać dużego bloku ciągłej pamięci w celu zwiększenia zarządzanych stert. Może to prowadzić do stanu OOM.

Fragmentacja w przestrzeni va jest często spowodowana przez co najmniej jeden z następujących scenariuszy:

  • Ładowanie tych samych zestawów do wielu domen aplikacji.

    Jeśli chcesz użyć zestawu w więcej niż jednej aplikacji działającej w tej samej puli aplikacji, nazwij zestaw i zainstaluj go w usłudze GAC. W ten sposób upewnij się, że zestaw jest ładowany do procesu tylko raz.

  • Uruchamianie aplikacji w środowisku produkcyjnym z atrybutem debugowania elementu ustawionym <compilation> na true.

    • Atrybut debugowania <compilation> elementu powinien znajdować się false w środowisku produkcyjnym.
    • Konfiguracja <deploy retail="true" /> umożliwia upewnienie się, że debugowanie jest zawsze wyłączone w produkcie. Aby uzyskać więcej informacji, zobacz Deployment Element (schemat ustawień ASP.NET).
  • Używanie skryptów w ramach przekształceń języka XSL (eXtensible Style sheet Language) lub tworzenia XmlSerializers.

    W tym przypadku zestawy dynamiczne spowodowane przez skrypty przekształcenia języka arkusza stylów rozszerzalnych (XSLT) lub XmlSerializers.

Zwracanie dużych zestawów danych

W przypadku korzystania z danych z bazy danych lub innego źródła danych ważne jest ograniczenie ilości zwracanych danych. Na przykład buforowanie wyniku zapytania zwracającego całą tabelę bazy danych w celu uniknięcia kosztów pobierania części danych z bazy danych w razie potrzeby nie jest dobrym rozwiązaniem. Może to łatwo spowodować dużą ilość pamięci i prowadzić do stanu OOM. Zezwolenie użytkownikowi na uruchomienie podobnego zapytania jest kolejnym typowym sposobem tworzenia sytuacji z dużą ilością pamięci. Na przykład zwróć wszystkich pracowników w firmie lub wszystkich klientów w stanie Teksas z nazwiskiem zaczynającym się od litery S.

Zawsze ograniczaj ilość danych, które mogą być zwracane z bazy danych. Nie zezwalaj na zapytania, takie jak SELECT * FROM. . . , ponieważ nie masz kontroli nad ilością danych wyświetlanych na stronie.

Równie ważne jest, aby upewnić się, że nie wyświetlasz dużych wyników danych w elementach interfejsu użytkownika, takich jak kontrolka GridView. Oprócz pamięci wymaganej dla zwracanych danych będziesz również korzystać z dużych ilości danych w ciągach i w elementach interfejsu użytkownika, które są wymagane do renderowania wyników. Implementując stronicowanie i walidację danych wejściowych, aby nie zwracać dużych zestawów danych, można uniknąć tego problemu.

Uruchamianie w środowisku produkcyjnym z włączonym śledzeniem

ASP.NET śledzenie to zaawansowana funkcja rozwiązywania problemów z aplikacjami. Ale nigdy nie powinno być pozostawione w środowisku produkcyjnym. ASP.NET śledzenia używa struktur danych, takich jak DataTables do przechowywania informacji o śledzeniu, a wraz z upływem czasu mogą powodować wysoki stan pamięci, który może prowadzić do OOM.

Śledzenie powinno zostać wyłączone w środowisku produkcyjnym. Można to zrobić, ustawiając enabled atrybut elementu na <trace> wartość false w pliku web.config . Włączenie wdrożenia handlu detalicznego przy użyciu powoduje <deploy retail="true" /> również wyłączenie śledzenia w aplikacjach.

Wyciek zasobów natywnych

Wiele zarządzanych zasobów będzie również korzystać z zasobów natywnych. Ponieważ usługa GC nie czyści zasobów natywnych, deweloper jest odpowiedzialny za implementowanie i wywoływanie metody Dispose w celu oczyszczenia zasobów natywnych. Jeśli używasz typu implementującego IDisposable interfejs i nie wywołujesz Dispose metody, istnieje ryzyko wycieku zasobów natywnych i spowodowania stanu OOM.

Te obiekty powinny zaimplementować iDisposable interfejs i należy wywołać Dispose metodę na tych obiektach, gdy nie są już potrzebne.