Dela via


Automatisk minneshantering

Automatisk minneshantering är en av de tjänster som Common Language Runtime tillhandahåller under hanterad körning. Common Language Runtimes skräpinsamlare hanterar allokering och frigörande av minne för ett program. För utvecklare innebär det att du inte behöver skriva kod för att utföra minneshanteringsuppgifter när du utvecklar hanterade program. Automatisk minneshantering kan eliminera vanliga problem, till exempel att glömma att frigöra ett objekt och orsaka en minnesläcka eller försöka komma åt minnet för ett objekt som redan har frigjorts. I det här avsnittet beskrivs hur skräpinsamlaren allokerar och frigör minne.

Allokera minne

När du initierar en ny process reserverar körningen en sammanhängande region med adressutrymme för processen. Det här reserverade adressutrymmet kallas för den hanterade heapen. Den hanterade heapen behåller en pekare till adressen där nästa objekt i heapen ska allokeras. Inledningsvis är den här pekaren inställd på den hanterade heapens basadress. Alla referenstyper allokeras på den hanterade heapen. När ett program skapar den första referenstypen allokeras minne för typen på basadressen för den hanterade heapen. När programmet skapar nästa objekt allokerar skräpinsamlaren minne för det i adressutrymmet direkt efter det första objektet. Så länge adressutrymmet är tillgängligt fortsätter skräpinsamlaren att allokera utrymme för nya objekt på det här sättet.

Det går snabbare att allokera minne från den hanterade heapen än ohanterad minnesallokering. Eftersom körningen allokerar minne för ett objekt genom att lägga till ett värde i en pekare är det nästan lika snabbt som att allokera minne från stacken. Eftersom nya objekt som allokeras i följd lagras sammanhängande i den hanterade heapen kan ett program dessutom komma åt objekten mycket snabbt.

Frigöra minne

Optimeringsmotorn för skräpinsamlaren avgör den bästa tiden för att utföra en samling baserat på de allokeringar som görs. När skräpinsamlaren utför en samling frigörs minnet för objekt som inte längre används av programmet. Den avgör vilka objekt som inte längre används genom att undersöka programmets rötter. Varje program har en uppsättning rötter. Varje rot refererar antingen till ett objekt på den hanterade heapen eller är inställt på null. Ett programs rötter omfattar statiska fält, lokala variabler och parametrar i en tråds stack och CPU-register. Skräpinsamlaren har åtkomst till listan över aktiva rötter som JIT-kompilatorn (just-in-time) och runtime underhåller. Med hjälp av den här listan undersöker den ett programs rötter och skapar samtidigt ett diagram som innehåller alla objekt som kan nås från rötterna.

Objekt som inte finns i diagrammet kan inte nås från programmets rötter. Skräpinsamlaren tar hänsyn till skräp som inte kan nås och frigör det minne som allokerats för dem. Under en samling undersöker skräpinsamlaren den hanterade heapen och letar efter de block av adressutrymme som upptas av oåtkomliga objekt. När det upptäcker varje oåtkomligt objekt använder det en minneskopieringsfunktion för att komprimera de åtkomliga objekten i minnet, vilket frigör blocken med adressutrymmen som allokerats till oåtkomliga objekt. När minnet för de nåbara objekten har komprimerats gör skräpinsamlaren nödvändiga pekarkorrigeringar så att programmets rötter pekar mot objekten på deras nya platser. Den placerar också den hanterade heapens pekare efter det sista nåbara objektet. Observera att minnet endast komprimeras om en samling identifierar ett stort antal objekt som inte kan nås. Om alla objekt i den hanterade heapen överlever en samling finns det inget behov av minneskomprimering.

För att förbättra prestandan allokerar körningen minne för stora objekt i en separat heap. Skräpinsamlaren frigör automatiskt minnet för stora objekt. Men för att undvika att flytta stora objekt i minnet komprimeras inte det här minnet.

Generationer och prestanda

För att optimera skräpinsamlarens prestanda delas den hanterade heapen in i tre generationer: 0, 1 och 2. Körningens skräpinsamlingsalgoritm baseras på flera generaliseringar som datorprogramindustrin har upptäckt är sanna genom att experimentera med skräpinsamlingsscheman. För det första är det snabbare att komprimera minnet för en del av den hanterade heapen än för hela den hanterade heapen. För det andra har nyare objekt kortare livslängd och äldre objekt har längre livslängd. Slutligen tenderar nyare objekt att vara relaterade till varandra och nås av programmet ungefär samtidigt.

Körningens skräpinsamlare lagrar nya objekt i generation 0. Objekt som skapas tidigt i programmets livslängd som överlever samlingar befordras och lagras i generation 1 och 2. Processen för objekthöjning beskrivs senare i det här avsnittet. Eftersom det går snabbare att komprimera en del av den hanterade heapen än hela heapen gör det här schemat att skräpinsamlaren kan frigöra minnet i en viss generation i stället för att frigöra minnet för hela den hanterade heapen varje gång den utför en samling.

I verkligheten utför skräpinsamlaren en samling när generation 0 är full. Om ett program försöker skapa ett nytt objekt när generation 0 är full upptäcker skräpinsamlaren att det inte finns något adressutrymme kvar i generation 0 att allokera för objektet. Skräpinsamlaren utför en samling i ett försök att frigöra adressutrymme i generation 0 för objektet. Skräpinsamlaren börjar med att undersöka objekten i generation 0 i stället för alla objekt i den hanterade heapen. Det här är den mest effektiva metoden eftersom nya objekt tenderar att ha kort livslängd och det förväntas att många av objekten i generation 0 inte längre används av programmet när en samling utförs. Dessutom återtar en samling enbart generation 0 ofta tillräckligt med minne för att programmet ska kunna fortsätta att skapa nya objekt.

När skräpinsamlaren utför en samling av generation 0 komprimeras minnet för de åtkomliga objekten enligt beskrivningen i Frigöra minne tidigare i det här avsnittet. Skräpinsamlaren befordrar sedan dessa objekt och tar hänsyn till den här delen av den hanterade heapgenerationen 1. Eftersom objekt som överlever samlingar tenderar att ha längre livslängd är det klokt att höja upp dem till en högre generation. Därför behöver skräpinsamlaren inte återanvända objekten i generation 1 och 2 varje gång den utför en samling av generation 0.

När skräpinsamlaren utför sin första samling av generation 0 och befordrar de nåbara objekten till generation 1, tar den hänsyn till resten av den hanterade heapgenerationen 0. Det fortsätter att allokera minne för nya objekt i generation 0 tills generation 0 är full och det är nödvändigt att utföra en annan samling. Nu avgör skräpinsamlarens optimeringsmotor om det är nödvändigt att undersöka objekten i äldre generationer. Om till exempel en samling av generation 0 inte frigör tillräckligt med minne för att programmet ska kunna slutföra sitt försök att skapa ett nytt objekt, kan skräpinsamlaren utföra en samling av generation 1 och sedan generation 2. Om detta inte hämtar tillräckligt med minne kan skräpinsamlaren utföra en samling av generation 2, 1 och 0. Efter varje samling komprimerar skräpinsamlaren de nåbara objekten i generation 0 och befordrar dem till generation 1. Objekt i generation 1 som överlever samlingar befordras till generation 2. Eftersom skräpinsamlaren bara stöder tre generationer finns objekt i generation 2 som överlever en samling kvar i generation 2 tills de är fast beslutna att inte kunna nås i en framtida samling.

Frigöra minne för ohanterade resurser

För de flesta objekt som programmet skapar kan du förlita dig på att skräpinsamlaren automatiskt utför de nödvändiga minneshanteringsuppgifterna. Ohanterade resurser kräver dock explicit rensning. Den vanligaste typen av ohanterad resurs är ett objekt som omsluter en operativsystemresurs, till exempel ett filhandtag, ett fönsterhandtag eller en nätverksanslutning. Även om skräpinsamlaren kan spåra livslängden för ett hanterat objekt som kapslar in en ohanterad resurs, har den inte specifik kunskap om hur du rensar resursen. När du skapar ett objekt som kapslar in en ohanterad resurs rekommenderar vi att du anger nödvändig kod för att rensa den ohanterade resursen i en offentlig rensningsmetod . Genom att tillhandahålla en Dispose-metod gör du det möjligt för användare av objektet att uttryckligen frigöra dess minne när de är klara med objektet. När du använder ett objekt som kapslar in en ohanterad resurs bör du vara medveten om Att ta bort och anropa den efter behov. Mer information om hur du rensar ohanterade resurser och ett exempel på ett designmönster för implementering av Kassera finns i Skräpinsamling.

Se även