Stack versus heap: geheugengebieden vergeleken

Een draaiend programma gebruikt voor data vooral twee geheugengebieden. De stack bevat de frames van functieaanroepen: lokale variabelen, argumenten en returnadressen. Hij wordt automatisch beheerd en is zeer snel, maar klein en gebonden aan de levensduur van de functieaanroep. De heap bevat geheugen dat het programma expliciet opvraagt, bijvoorbeeld met malloc of new, en dat blijft bestaan tot het wordt vrijgegeven. De heap is flexibel en groot, maar langzamer, en daar ontstaan geheugenlekken en fragmentatie.

KenmerkStackHeap
DoelFrames van functieaanroepen. Lokale variabelen, argumenten, returnadressenDynamisch toegewezen data die langer leeft dan een functie of waarvan de grootte vooraf onbekend is
ToewijzingAutomatisch. De stackpointer verschuift bij elke aanroep en terugkeerExpliciet. malloc en free, new en delete, of een garbage collector
SnelheidZeer snel. Alleen een pointer verschuivenLangzamer. De allocator zoekt een vrij blok en houdt administratie bij
LevensduurTot de functie terugkeertTot het geheugen wordt vrijgegeven (of door de garbage collector wordt opgeruimd)
GrootteBeperkt (meestal enkele MB per thread)Alleen beperkt door het beschikbare geheugen
LiggingGroeit meestal omlaag vanaf hoge adressenGroeit meestal omhoog vanaf lage adressen. De twee groeien naar elkaar toe
FragmentatieGeenMogelijk. Herhaald toewijzen en vrijgeven laat onbruikbare gaten achter
ThreadsElke thread heeft een eigen stackGedeeld door alle threads, dus de allocator moet thread-safe zijn
Veelvoorkomende foutenStack overflow door diepe recursie of grote lokale arraysGeheugenlekken, use-after-free, double free, onvoldoende geheugen

De getalswaarden zijn typische ordegroottes en variëren per procesgeneratie en product.

Wanneer welke variant?

Stack
Kleine, kortlevende data waarvan de grootte bij het compileren bekend is. Gewone lokale variabelen.
Heap
Grote data, data waarvan de grootte tijdens runtime verandert, en data die langer moet leven dan de functie die haar maakte.

Veelgestelde vragen

Waarom is toewijzen op de stack sneller dan op de heap?

Op de stack hoeft alleen de stackpointer te verschuiven, terwijl bij de heap een vrij blok moet worden gezocht en de administratie van de allocator moet worden bijgewerkt.

Wat veroorzaakt een stack overflow?

Te veel stackgebruik. Diepe of oneindige recursie of zeer grote lokale arrays overschrijden de grootte van de stack, waarna het programma crasht.

Wat is een geheugenlek?

Heapgeheugen dat is toegewezen maar niet wordt vrijgegeven, zodat het niet opnieuw kan worden gebruikt. Bij voortdurende lekken groeit het geheugengebruik van het programma steeds verder.

Delen threads de stack?

Nee. Elke thread heeft een eigen stack, terwijl de heap door alle threads wordt gedeeld.

Test uw begrip

Welk voordeel is juist van de indeling waarin heap en stack van een proces in tegengestelde richting groeien met ertussen een breed vrij gebied?

  1. AOok als vooraf niet bekend is hoeveel elk van beide gebruikt, kan de vrije ruimte flexibel worden gedeeld
  2. BHeap en stack kunnen tegelijk dezelfde adressen gebruiken, dus het geheugen verdubbelt
  3. CHet overlopen van de stack gebeurt nooit meer
Antwoord en toelichting tonen

Antwoord: A. Ook als vooraf niet bekend is hoeveel elk van beide gebruikt, kan de vrije ruimte flexibel worden gedeeld

De heap groeit naar hogere en de stack naar lagere adressen, dus de ruimte ertussen is bruikbaar zowel voor programma's die veel heap gebruiken als voor programma's die veel stack gebruiken. Als aan elk een vaste grootte wordt toegewezen, kan het ene overlopen terwijl het andere ruimte over heeft. De stack heeft echter vaak een bovengrens, en te diepe recursie overschrijdt die en geeft een stack overflow.

Uit welk gebied van het proces wordt het met malloc in C gereserveerde geheugen in het algemeen genomen?

  1. ADe .text-sectie
  2. BDe heap
  3. CDe .rodata-sectie
Antwoord en toelichting tonen

Antwoord: B. De heap

De heap is het gebied waar tijdens uitvoering van het programma geheugen van de benodigde grootte wordt gereserveerd en met free wordt teruggegeven als het niet meer nodig is. Kenmerkend is dat grootte en levensduur tijdens uitvoering kunnen worden bepaald. malloc beheert zelf het vrije gebied en vraagt bij tekort het OS het gebied uit te breiden (op Linux met de systeemaanroepen brk of mmap). Als men vergeet free uit te voeren, blijft ongebruikt geheugen bestaan: een geheugenlek.

Welke volgorde van de geheugenindeling van een gangbaar proces op Linux en dergelijke, van laag naar hoog adres, is juist?

  1. ATekst → data → BSS → heap → (vrij) → stack
  2. BStack → tekst → heap → data → BSS
  3. CHeap → stack → BSS → data → tekst
Antwoord en toelichting tonen

Antwoord: A. Tekst → data → BSS → heap → (vrij) → stack

In de typische indeling staan aan de lage adreszijde de machinecode (tekst), de data met beginwaarde en de BSS die met 0 begint, waarboven de heap naar hogere adressen groeit. De stack groeit van de hoge adreszijde naar lagere adressen, en in het brede vrije gebied daartussen liggen gedeelde bibliotheken en mmap-gebieden. De werkelijke adressen worden voor de veiligheid door ASLR bij elke uitvoering verschoven.

  1. Wat gebeurt er in het algemeen op Linux en dergelijke als recursie te diep wordt en de bovengrens van de stack wordt overschreden?
  2. Bij herhaald malloc en free kan het voorkomen dat, ook al is het totaal aan vrij geheugen ruim voldoende, een groot gebied niet meer in één keer kan worden gereserveerd. Hoe heet dit verschijnsel?
  3. Wat gebeurt er als men bij het schrijven van een recursieve functie die zichzelf aanroept in RISC-V vergeet ra op de stack op te slaan?

Oefenvragen over Linker, loader en uitvoerbare formaten

Belangrijke termen uitgelegd

Stackframe
Gebied dat bij elke functieaanroep op de stack wordt gereserveerd. Bevat het terugkeeradres, opgeslagen registers, lokale variabelen en dergelijke en wordt bij het terugkeren uit de functie vrijgegeven.
Stackpointer
Register dat wijst naar het adres van de huidige top van de stack. In RISC-V is dit sp (x2). In veel ISA's groeit de stack naar lagere adressen.
Heap
Geheugengebied waaruit tijdens het draaien van een programma met onder meer malloc geheugen van de benodigde grootte wordt gereserveerd en met free weer wordt teruggegeven. Herhaald reserveren en vrijgeven kan fragmentatie veroorzaken.

Verwante vergelijkingen