Dela via


Felsöka problem med slut på minne (System.OutOfMemoryException) i ASP.NET

Den här artikeln hjälper dig att felsöka fel med slut på minne i ASP.NET.

Ursprunglig produktversion: ASP.NET
Ursprungligt KB-nummer: 2020006

Symptom

Ett av de vanligaste problemen som vi ser i Microsofts kundtjänst är OutOfMemoryException scenarier. Därför har vi sammanställt en samling resurser som hjälper dig att felsöka och identifiera orsaken till minnesproblem.

Innan vi går igenom information om hur du felsöker en OutOfMemoryExceptionär det viktigt att du förstår vad som orsakar det här problemet. I motsats till vad många utvecklare tror påverkar mängden RAM-minne som är installerat inte risken för en OutOfMemoryException. Ett 32-bitars operativsystem kan hantera 4 GB virtuellt adressutrymme, oavsett hur mycket fysiskt minne som är installerat i rutan. Av detta är 2 GB reserverat för operativsystemet (kernellägesminne) och 2 GB allokeras till användarlägesprocesser. De 2 GB som allokerats för kernellägesminne delas mellan alla processer, men varje process får sina egna 2 GB adressutrymme i användarläge. Allt förutsätter att du inte kör med växeln /3gb aktiverad.

När ett program behöver använda minne reserverar det en del av det virtuella adressutrymmet och checkar sedan in minne från det segmentet. Det är precis vad .NET Framework skräpinsamlare (GC) gör när det behövs minne för att utöka de hanterade högarna. När GC behöver ett nytt segment för den lilla objekthögen (där objekt som är mindre än 85 kB finns) gör den en allokering på 64 MB. När det behövs ett nytt segment för den stora objekt-heapen gör den en allokering på 16 MB. Dessa stora allokeringar måste uppfyllas från sammanhängande block av de 2 GB adressutrymme som processen måste arbeta med. Om operativsystemet inte kan uppfylla GC:s begäran om ett sammanhängande minnesblock inträffar en System.OutOfMemoryException (OOM).

Obs!

En 32-bitarsprocess som körs på ett 64-bitarssystem kan hantera 4 GB användarlägesminne och en 64-bitarsprocess som körs på ett 64-bitars åtgärdssystem kan hantera 8 TB användarlägesminne, så en OOM på ett 64-bitars åtgärdssystem är inte troligt. Det är möjligt att uppleva en OOM i en 32-bitarsprocess som körs på ett 64-bitarssystem, men det sker vanligtvis inte förrän processen använder nära 3 GB privata byte.

Det finns två orsaker till varför du kan se ett OOM-villkor.

  1. Processen använder mycket minne (vanligtvis över 800 MB i en 32-bitarsmiljö.)
  2. Det virtuella adressutrymmet är fragmenterat, vilket minskar sannolikheten för att en stor, sammanhängande allokering lyckas.

Det går också att se ett OOM-villkor på grund av en kombination av 1 och 2. Mer information finns i Felsöka System.OutOfMemoryExceptions i ASP.NET.

När ett OOM inträffar kan du märka ett eller flera av följande symtom:

När det gäller att fastställa orsaken till ett OOM-villkor arbetar du faktiskt med att fastställa orsaken till antingen en hög minnessituation eller ett fragmenterat adressutrymme. Vi kan inte dokumentera alla möjliga orsaker till dessa situationer, men det finns några vanliga orsaker som vi ser regelbundet.

I följande information beskrivs vanliga orsaker till OOM-villkor och lösningar för att lösa var och en av dessa orsaker.

Sammanfogning av strängar

Strängar i ett hanterat program (ett program som skrivits med hjälp av .NET Framework) är oföränderliga. När ett nytt värde tilldelas till en sträng skapas en kopia av den befintliga strängen. Och det nya värdet tilldelas till den nya strängen. Det orsakar vanligtvis inga problem. Men när ett stort antal strängar sammanfogas orsakar det många fler strängallokeringar än vad en utvecklare kanske inser. Och det kan leda till minnestillväxt och OOM-villkor.

Om du vill undvika OOM på grund av strängsammanfogning kontrollerar du att du använder StringBuilder klassen . Mer information finns i Så här förbättrar du prestanda för strängsammanfogning i Visual C#.

Fragmentering i den hanterade heapen

Skräpinsamlaren (GC) i ett hanterat program komprimerar heaps för att minska mängden fragmentering. Det går dock att fästa objekt i ett hanterat program. Fästa objekt kan inte flyttas under heapkomprimering. Om du gör det ändras adressen där objektet finns. Om ett program fäster ett stort antal objekt och/eller fäster objekt under lång tid kan det orsaka fragmentering i den hanterade heapen. Det kan leda till att GC växer den hanterade heapen oftare och orsakar ett OOM-tillstånd.

Vi har arbetat med att minimera OOM-villkor på grund av fästning sedan .NET Framework 1.0. Inkrementella förbättringar har gjorts i varje version. Det finns dock fortfarande designmönster som du kan implementera som är bra om du behöver fästa objekt.

Fragmentering i va-utrymmet (Virtual Address)

Varje process har en viss mängd minne allokerat till sig, och det minnet representerar VA-utrymmet för processen. Om VA-utrymmet blir fragmenterat ökar sannolikheten för att GC inte kan hämta ett stort block med sammanhängande minne för att utöka de hanterade heapsna. Och det kan leda till ett OOM-villkor.

Fragmentering i VA-utrymmet orsakas ofta av ett eller flera av följande scenarier:

  • Läser in samma sammansättningar i flera programdomäner.

    Om du behöver använda en sammansättning i mer än ett program som körs i samma programpool ska du ge sammansättningen ett starkt namn och installera den i GAC. Genom att göra det ser du till att sammansättningen bara läses in i processen en gång.

  • Köra ett program i produktion med felsökningsattributet för elementet inställt på <compilation>true.

    • Felsökningsattributet för -elementet ska vara när det <compilation> är false i produktion.
    • Du kan använda konfigurationen <deploy retail="true" /> för att säkerställa att felsökning alltid är inaktiverat i produkten. Mer information finns i distributionselement (ASP.NET inställningsschema).
  • Användning av skript i XSL(eXtensible Style sheet Language) transformerar eller skapar XmlSerializers.

    I det här fallet, dynamiska sammansättningar som orsakas av XSLT-skript (Extensible Style Sheet Language Transformations) eller XmlSerializers.

Returnera stora datauppsättningar

När du använder data från en databas eller annan datakälla är det viktigt att begränsa mängden data som returneras. Till exempel är cachelagring av ett frågeresultat som returnerar en hel databastabell för att undvika kostnaden för att hämta delar av data från databasen när det behövs inte en bra metod. Detta kan enkelt orsaka högt minne och leda till ett OOM-villkor. Att låta en användare starta en liknande fråga är ett annat vanligt sätt att skapa en hög minnessituation. Returnera till exempel alla anställda i ett företag eller alla kunder i delstaten Texas med ett efternamn som börjar med bokstaven S.

Begränsa alltid mängden data som kan returneras från en databas. Tillåt inte frågor, till exempel eftersom du då inte har någon kontroll över hur mycket data som SELECT * FROM. . . visas på sidan.

Det är lika viktigt att se till att du inte visar ett stort dataresultat i gränssnittselement, till exempel GridView-kontrollen. Förutom det minne som krävs för returnerade data kommer du också att använda stora mängder data i strängar och i gränssnittselement som krävs för att återge resultaten. Genom att implementera sidindelning och validering av indata så att stora datamängder inte returneras kan du undvika det här problemet.

Köra i en produktionsmiljö med spårning aktiverat

ASP.NET spårning är en kraftfull funktion för felsökning av program. Men det bör aldrig lämnas kvar i en produktionsmiljö. ASP.NET spårning använder datastrukturer som DataTables att lagra spårningsinformation, och med tiden kan de orsaka ett högt minnestillstånd som kan leda till OOM.

Spårning bör inaktiveras i en produktionsmiljö. Du kan göra det genom att ange enabled attributet för elementet <trace> till false i web.config-filen . Om du aktiverar distribution i detaljhandeln med hjälp <deploy retail="true" /> av inaktiveras även spårning i dina program.

Läcka interna resurser

Många hanterade resurser använder också inbyggda resurser. Eftersom GC inte rensar interna resurser ansvarar en utvecklare för att implementera och anropa metoden Dispose för att rensa interna resurser. Om du använder en typ som implementerar IDisposable gränssnittet och du inte anropar Dispose metoden riskerar du att läcka interna resurser och orsaka ett OOM-villkor.

Dessa objekt bör implementera iDisposable gränssnittet och du bör anropa Dispose metoden för dessa objekt när du inte längre behöver dem.