Forstå minnetildeling i Delphi
Getty Images/Daniel Sambraus
Kalle funksjonen 'DoStackOverflow' en gang fra koden din og du får EStackOverflow feil reist av Delphi med meldingen 'stack overflow'.
|_+_||_+_||_+_|
|_+_|
|_+_|
Hva er denne 'stabelen' og hvorfor er det overløp der ved å bruke koden ovenfor?
Så DoStackOverflow-funksjonen kaller seg selv rekursivt -- uten en 'exit-strategi' -- den fortsetter bare å snurre og avslutter aldri.
En rask løsning, du ville gjort, er å fjerne den åpenbare feilen du har, og sikre at funksjonen eksisterer på et tidspunkt (slik at koden din kan fortsette å kjøre fra der du har kalt funksjonen).
Du går videre, og du ser deg aldri tilbake, og bryr deg ikke om feilen/unntaket slik det nå er løst.
Likevel gjenstår spørsmålet: hva er denne stabelen og hvorfor er det overløp ?
Minne i Delphi-applikasjonene dine
Når du begynner å programmere i Delphi, kan det hende du opplever en feil som den ovenfor, du vil løse den og gå videre. Denne er relatert til minnetildeling. Mesteparten av tiden vil du ikke bry deg om minnetildeling så lenge du frigjør det du lager .
Etter hvert som du får mer erfaring i Delphi, begynner du å lage dine egne klasser, instansierer dem, bryr deg om minnehåndtering og lignende.
Du vil komme til det punktet hvor du vil lese, i Hjelp, noe sånt som 'Lokale variabler (deklarert i prosedyrer og funksjoner) ligger i en applikasjons stable .' og også Klasser er referansetyper, så de blir ikke kopiert på oppgave, de er bestått ved referanse, og de tildeles på haug .
Så, hva er 'stack' og hva er 'heap'?
Stack vs. Heap
Kjører programmet på Windows , er det tre områder i minnet der applikasjonen din lagrer data: globalt minne, heap og stack.
Globale variabler (deres verdier/data) lagres i det globale minnet. Minnet for globale variabler er reservert av applikasjonen når programmet starter og forblir allokert til programmet avsluttes. Minnet for globale variabler kalles 'datasegment'.
Siden globalt minne bare blir tildelt og frigjort ved programavslutning, bryr vi oss ikke om det i denne artikkelen.
Stack og heap er der dynamisk minneallokering finner sted: når du oppretter en variabel for en funksjon, når du oppretter en forekomst av en klasse når du sender parametere til en funksjon og bruker/passer dens resultatverdi.
Hva er Stack?
Når du erklærer en variabel inne i en funksjon, tildeles minnet som kreves for å holde variabelen fra stabelen. Du skriver ganske enkelt 'var x: heltall', bruker 'x' i funksjonen din, og når funksjonen avsluttes, bryr du deg ikke om minneallokering eller frigjøring. Når variabelen går utenfor scope (koden går ut av funksjonen), frigjøres minnet som ble tatt på stabelen.
Stabelminnet tildeles dynamisk ved å bruke LIFO-tilnærmingen ('sist inn først ut').
I Delphi-programmer , brukes stabelminne av
- Lokale rutinevariabler (metode, prosedyre, funksjon).
- Rutineparametere og returtyper.
- Windows API-funksjonsamtaler.
- Poster (dette er grunnen til at du ikke eksplisitt trenger å opprette en forekomst av en posttype).
Du trenger ikke eksplisitt frigjøre minnet på stabelen, da minnet blir automagisk allokert for deg når du for eksempel erklærer en lokal variabel til en funksjon. Når funksjonen avsluttes (noen ganger til og med før på grunn av Delphi-kompilatoroptimalisering) vil minnet for variabelen bli automagisk frigjort.
Stable minnestørrelseer som standard store nok for dine (så komplekse som de er) Delphi-programmer. Verdiene 'Maksimal stabelstørrelse' og 'Minimum stabelstørrelse' på Linker-alternativene for prosjektet ditt spesifiserer standardverdier - i 99,99 % trenger du ikke å endre dette.
Tenk på en stabel som en haug med minneblokker. Når du erklærer/bruker en lokal variabel, vil Delphi memory manager velge blokken fra toppen, bruke den, og når den ikke lenger er nødvendig, vil den bli returnert tilbake til stabelen.
Ved å ha lokalt variabelminne brukt fra stabelen, initialiseres ikke lokale variabler når de deklareres. Erklær en variabel 'var x: heltall' i en funksjon og bare prøv å lese verdien når du skriver inn funksjonen -- x vil ha en 'rar' verdi som ikke er null. Så initialiser (eller sett verdi) alltid til dine lokale variabler før du leser verdien deres.
På grunn av LIFO er stack (minnetildeling) operasjoner raske, da bare noen få operasjoner (push, pop) kreves for å administrere en stack.
Hva er Heap?
En haug er et minneområde der dynamisk tildelt minne er lagret. Når du oppretter en forekomst av en klasse, tildeles minnet fra haugen.
I Delphi-programmer brukes heap-minne av/når
- Opprette en forekomst av en klasse.
- Opprette og endre størrelse på dynamiske matriser.
- Eksplisitt allokering av minne ved hjelp av GetMem, FreeMem, New og Dispose().
- Bruker ANSI/wide/Unicode-strenger, varianter, grensesnitt (administrert automatisk av Delphi).
Heap-minne har ingen fin layout der det ville være en viss rekkefølge på å tildele minneblokker. Heap ser ut som en boks med klinkekuler. Minnetildeling fra haugen er tilfeldig, en blokk herfra enn en blokk derfra. Dermed er heap-operasjoner litt tregere enn de på stabelen.
Når du ber om en ny minneblokk (dvs. oppretter en forekomst av en klasse), vil Delphi Memory Manager håndtere dette for deg: du får en ny minneblokk eller en brukt og kassert.
Haugen består av alt virtuelt minne ( RAM og diskplass ).
Manuell tildeling av minne
Nå som alt om minne er klart, kan du trygt (i de fleste tilfeller) ignorere ovenstående og ganske enkelt fortsette å skrive Delphi-programmer som du gjorde i går.
Selvfølgelig bør du være klar over når og hvordan du manuelt allokerer/frigjør minne.
'EStackOverflow' (fra begynnelsen av artikkelen) ble hevet fordi med hvert kall til DoStackOverflow har et nytt minnesegment blitt brukt fra stabelen og stabelen har begrensninger. Så enkelt som det.