Hur Fungerar Minnet Förvaltning Arbetar i C#?

0
931

Jämfört med C++, C#’s garbage collector verkar som magi, och du kan enkelt skriva kod utan att behöva oroa underliggande minne. Men om du bryr dig om prestanda, att veta hur .NET runtime hanterar sin RAM kan hjälpa dig att skriva bättre kod.

Värdet Typer vs Referens Typer

Det finns två typer av typer i .NETTO, vilket direkt påverkar hur den underliggande minne hanteras.

Värdet typer är primitiva typerna med fasta storlekar som int, bool, float, double, etc. De är förbi värde, vilket innebär att om du ringer someFunction(int arg), argumentet kopieras och skickas över som ny plats i minnet.

Under huven, värde slag är (oftast) som lagras på stacken. Detta gäller mest för att lokala variabler, och det finns massor av undantag där de ska istället lagras på heap. Men i alla fall, den plats i minnet där värdet av den typ som är bosatt innehar det verkliga värdet av denna variabel.

Stacken är bara en speciell plats i minnet, initieras med ett default-värde utan möjlighet att expandera. Stacken är en Sist in, Först ut (LIFO) datastruktur. Du kan se det som en hink—variabler läggs till överst i hinken, och när de gå ut i räckvidd .NÄTET når i hinken och tar bort dem ett i taget tills det blir till botten.

Stacken är mycket snabbare, men det är fortfarande bara en plats i RAM-minnet, inte en speciell plats i CPU-cache (även om det är mindre än högen, och som sådan är mycket sannolikt att bli varm i cache-minnet, som hjälper till med prestanda).

Stacken får de flesta av dess prestanda från sin LIFO-struktur. När du anropar en funktion, kommer alla variabler som definieras i denna funktion läggs till stacken. När denna funktion returnerar och de variabler gå ut räckvidd, stack rensar bort allt som funktion sätta på den. Runtime-modulen hanterar detta med stack ramar som definierar block minne för olika funktioner. Stack anslagen är mycket snabb, eftersom det är bara skriva ett enda värde till slut stack frame.

Det är också där termen “StackOverflow” kommer från, vilket resulterar när en funktion innehåller alltför många nästlade metod för samtal och fyller upp hela bunten.

Referens typer, dock är antingen för stora, inte har fasta storlekar, eller leva för lång för att vara på stacken. Vanligtvis, detta sker i form av objekt och klasser som har varit aktuella, men de innehåller också arrayer och strängar, som kan variera i storlek.

Referens typer som instanser av klasser är ofta initieras med nya sökord, vilket skapar en ny instans av klassen och returnerar en referens till det. Du kan ställa in den här till en lokal variabel, som faktiskt använder stacken för att lagra hänvisning till den plats på högen.

Högen kan expandera och fylla upp tills datorn går ur minnet, vilket gör det utmärkt för att lagra en massa uppgifter. Men, det är oorganiserade, och i C# det måste hanteras med en sophämtare för att fungera korrekt. Högen anslagen är också långsammare än stack anslag, även om de fortfarande ganska snabbt.

Det finns dock ett antal undantag till dessa regler, annars värde-och referenstyper skulle kunna kallas för “stack typer” och “heap typer.”

  • Yttre variabler av lambda-funktioner, lokala variabler av IEnumerator block, och lokala variabler av asynkrona metoder är alla som lagras på heap.
  • Värde typ fält av klasser är långsiktiga variabler, och är alltid lagras på heap. De är också insvept i en typ av hänvisning och lagras tillsammans med som referens typ.
  • Statiska fält är också alltid lagras på heap.
  • Anpassade strukturer är värdet typer, men de kan innehålla referenser typer gillar Listor och strängar som är lagrade på högen som vanligt. Skapa en kopia av struct skapar en ny kopia och fördelningen av alla referenstyper på högen.

Det mest anmärkningsvärda undantaget av “referens typer som på högen,” är användningen av stackalloc med P<T>, som manuellt tilldelas ett block i minnet på en stack för en tillfällig array som kommer att rengöras av stapeln som vanligt när det går ut räckvidd. Detta kringgår en relativt dyr högen fördelning, och sätter mindre press på sophämtare i processen. Det kan vara en mycket mer prestanda, men det är lite av en avancerad funktion, så om du skulle vilja lära dig mer om det kan du läsa denna guide om hur man använder det på rätt sätt utan att orsaka en StackOverflow undantag.

Vad Är Garbage Collection?

Stacken är mycket organiserad, men högen är rörigt. Utan någonting att hantera saker och ting på högen inte får städas upp automatiskt, vilket leder till att din ansökan att köra slut på minne på grund av att det aldrig att bli befriade.

Det är naturligtvis ett problem, vilket är anledningen till sophämtare finns. Den körs på en bakgrund tråd och regelbundet skannar din ansökan för referenser som inte längre finns kvar på stacken, vilket tyder på att programmet har slutat att bry sig om de uppgifter som refereras. Den .NET runtime kan komma in och städa upp och skift-minne runt i processen för att göra leken mer organiserat.

Men, detta magiska kommer vid en kostnads—och sophämtning är långsam och dyr. Den körs på en bakgrund tråd, men det är en period där programmet måste stoppas för att köra garbage collection. Detta är den avvägning som kommer med programmering i C#, allt du kan göra är att försöka minimera de sopor som du skapar.

I språk utan en sophämtare, du behöver för att manuellt rensa upp efter dig själv, vilket är snabbare i många fall, utan mer irriterande för programmeraren. Så, i en mening, en sophämtare är som en Roomba, som rensar upp ditt golv automatiskt, men är långsammare än att bara komma upp och dammsuga.