Geheugentoewijzing begrijpen in Delphi

Noem de functie "DoStackOverflow" eenmaal vanuit uw code en u krijgt de EStackOverflow fout opgeworpen door Delphi met het bericht "stack overflow".


 functie DoStackOverflow: geheel getal;

beginnen

 resultaat: = 1 + DoStackOverflow;

einde;

Wat is deze "stapel" en waarom is er een overloop met de bovenstaande code?

Dus roept de DoStackOverflow-functie zichzelf recursief op - zonder een "exit-strategie" - hij blijft maar draaien en verlaat nooit.

Een snelle oplossing, zou je doen, is om de voor de hand liggende fout die je hebt te wissen en ervoor te zorgen dat de functie op een bepaald punt bestaat (zodat je code kan blijven uitvoeren vanaf waar je de functie hebt genoemd).

Je gaat verder, en je kijkt nooit terug, zonder je zorgen te maken over de bug / uitzondering zoals deze nu is opgelost.

Toch blijft de vraag: wat is deze stapel en waarom is er een overloop?

Geheugen in uw Delphi-toepassingen

Wanneer je begint met programmeren in Delphi, zou je een bug kunnen ervaren zoals hierboven, je zou het oplossen en verder gaan. Deze heeft te maken met geheugentoewijzing. Meestal geeft het u niet om geheugentoewijzing, zolang u maar vrijmaakt wat u maakt.

Naarmate je meer ervaring opdoet in Delphi, begin je je eigen klassen te maken, deze te instantiëren, zorg te dragen voor geheugenbeheer en dergelijke.

U komt op het punt waar u in de Help zoiets zult lezen "Lokale variabelen (gedeclareerd binnen procedures en functies) bevinden zich in een applicatie stack." en ook Klassen zijn referentietypes, dus ze worden niet gekopieerd bij toewijzing, ze worden doorgegeven door middel van verwijzing en ze worden toegewezen op de hoop.

Dus wat is "stack" en wat is "heap"?

Stapel versus hoop

Als u uw toepassing op Windows uitvoert, zijn er drie gebieden in het geheugen waar uw toepassing gegevens opslaat: globaal geheugen, heap en stapel.

Globale variabelen (hun waarden / gegevens) worden opgeslagen in het globale geheugen. Het geheugen voor globale variabelen wordt gereserveerd door uw toepassing wanneer het programma start en blijft toegewezen totdat uw programma eindigt. Het geheugen voor globale variabelen wordt "gegevenssegment" genoemd.

Aangezien wereldwijd geheugen slechts eenmaal wordt toegewezen en vrijgegeven bij het beëindigen van het programma, kunnen we dit in dit artikel niet schelen.

Stack en heap zijn waar dynamische geheugentoewijzing plaatsvindt: wanneer u een variabele voor een functie maakt, wanneer u een instantie van een klasse maakt wanneer u parameters naar een functie verzendt en de resultaatwaarde gebruikt / doorgeeft.

Wat is stapel?

Wanneer u een variabele binnen een functie declareert, wordt het geheugen dat nodig is om de variabele vast te houden toegewezen uit de stapel. U schrijft eenvoudigweg "var x: geheel getal", gebruikt "x" in uw functie en wanneer de functie wordt afgesloten, maakt u zich geen zorgen over geheugentoewijzing of vrijmaken. Wanneer de variabele buiten bereik valt (code verlaat de functie), wordt het geheugen vrijgemaakt dat op de stapel is genomen.

Het stapelgeheugen wordt dynamisch toegewezen met behulp van de LIFO-benadering ("last in first out").

In Delphi-programma's wordt stapelgeheugen gebruikt door

  • Lokale routine (methode, procedure, functie) variabelen.
  • Routineparameters en retourtypen.
  • Windows API-functie-aanroepen.
  • Records (daarom hoeft u niet expliciet een exemplaar van een recordtype te maken).

U hoeft het geheugen op de stapel niet expliciet vrij te maken, omdat het geheugen automatisch op magische wijze aan u wordt toegewezen wanneer u bijvoorbeeld een lokale variabele voor een functie declareert. Wanneer de functie wordt afgesloten (soms zelfs daarvoor vanwege optimalisatie van de Delphi-compiler), wordt het geheugen voor de variabele automatisch magisch vrijgegeven.

De geheugencapaciteit van de stapel is standaard groot genoeg voor uw (zo complexe als ze zijn) Delphi-programma's. De waarden "Maximale stapelgrootte" en "Minimale stapelgrootte" in de Linker-opties voor uw project specificeren standaardwaarden - in 99,99% hoeft u dit niet te wijzigen.

Zie een stapel als een stapel geheugenblokken. Wanneer u een lokale variabele declareert / gebruikt, kiest Delphi Memory Manager het blok van boven, gebruikt het en wanneer het niet langer nodig is, wordt het teruggezet naar de stapel.

Met gebruik van lokaal variabel geheugen uit de stapel, worden lokale variabelen niet geïnitialiseerd wanneer ze worden gedeclareerd. Verklaar een variabele "var x: geheel getal" in een functie en probeer gewoon de waarde te lezen wanneer u de functie invoert - x heeft een "vreemde" niet-nulwaarde. Dus initialiseer (of stel waarde) altijd in op uw lokale variabelen voordat u hun waarde leest.

Vanwege LIFO zijn stapelbewerkingen (geheugentoewijzing) snel omdat slechts een paar bewerkingen (push, pop) nodig zijn om een ​​stapel te beheren.

Wat is hoop?

Een heap is een geheugengebied waarin dynamisch toegewezen geheugen wordt opgeslagen. Wanneer u een instantie van een klasse maakt, wordt het geheugen toegewezen vanuit de heap.

In Delphi-programma's wordt heap-geheugen gebruikt door / wanneer

  • Een instantie van een klasse maken.
  • Dynamische arrays maken en vergroten of verkleinen.
  • Geheugen expliciet toewijzen met GetMem, FreeMem, New en Dispose ().
  • ANSI / wide / Unicode-strings, varianten, interfaces gebruiken (automatisch beheerd door Delphi).

Heap-geheugen heeft geen mooie lay-out waarbij er enige volgorde zou zijn om geheugenblokken toe te wijzen. Hoop ziet eruit als een blikje knikkers. Geheugentoewijzing van de heap is willekeurig, een blok vanaf hier dan een blok vanaf daar. Heap-bewerkingen zijn dus iets langzamer dan die op de stapel.

Wanneer u om een ​​nieuw geheugenblok vraagt ​​(d.w.z. een instantie van een klasse maakt), zal Delphi geheugenbeheer dit voor u regelen: u krijgt een nieuw geheugenblok of een gebruikt en weggegooid blok.

De heap bestaat uit al het virtuele geheugen (RAM en schijfruimte).

Geheugen handmatig toewijzen

Nu alles over geheugen duidelijk is, kun je veilig (in de meeste gevallen) het bovenstaande negeren en gewoon doorgaan met het schrijven van Delphi-programma's zoals je gisteren deed.

Natuurlijk moet u weten wanneer en hoe u handmatig geheugen kunt toewijzen / vrijmaken.

De "EStackOverflow" (vanaf het begin van het artikel) is verhoogd omdat bij elke aanroep naar DoStackOverflow een nieuw geheugensegment uit de stapel is gebruikt en de stapel beperkingen heeft. Zo simpel is het.