In Python wordt geheugen automatisch beheerd: bij het aanmaken van een object wordt geheugen toegewezen, en wanneer geen ander object meer naar het object verwijst, wordt het geheugen vrijgegeven. De belangrijkste strategie is referentietelling: aan elk object is een referentieteller gekoppeld; wanneer deze nul wordt, wordt het object verwijderd.
Echter, er kunnen cyclische verwijzingen ontstaan waarbij objecten naar elkaar verwijzen en hun referentieteller nooit naar nul daalt, zelfs niet als de verwijzingen uit externe code zijn verwijderd.
Om hiertegen te vechten, heeft Python (CPython) een garbage collector (GC) die cycli van objecten die niet toegankelijk zijn vanuit de roots detecteert en deze verwijdert. De garbage collector kan worden beheerd via de module gc.
Voorbeeld van een cyclus:
class Node: def __init__(self): self.ref = None n1 = Node() n2 = Node() n1.ref = n2 n2.ref = n1 del n1, n2 # objecten leven nog omdat er een cyclische verwijzing is!
Vraag: "Als je del obj aanroept, wordt het geheugen altijd onmiddellijk vrijgegeven. Is dit waar?"
Antwoord: Nee! De operatie del verwijdert slechts één verwijzing naar het object. Als er ergens (expliciet of cyclisch) andere verwijzingen blijven bestaan, zal het geheugen niet worden vrijgegeven totdat de referentieteller nul is en/of de GC de cycli verwijdert. Voorbeeld:
import gc class A: pass x = A() y = x del x # y heeft nog een verwijzing, het object leeft! del y # pas nu kan het object worden verwijderd
Verhaal
In een systeem voor het verwerken van grote bestanden werden gerelateerde objecten in geheugen gehouden. Na het verwijderen van externe verwijzingen werd het geheugen nog steeds niet vrijgegeven. De reden was cyclische verwijzingen die niet onmiddellijk werden schoongemaakt, en soms helemaal niet werden schoongemaakt door de aanwezigheid van de destructor __del__.
Verhaal
In een langdurig serverproces werden vaak grote tijdelijke structuren met cyclische grafen aangemaakt. De ontwikkelaars schakelden de tijdelijke foutopsporing van de GC niet uit, waardoor er zeldzame, maar merkbare pauzes optraden. Dit werd gediagnosticeerd als "prestatieverliezen". Het bleek dat de frequentie van garbage collection de schuldige was.
Verhaal
Bij de poort van een groot project naar PyPy (een alternatieve interpreter) was de oude code afhankelijk van het specifieke gedrag van CPython met betrekking tot onmiddellijke geheugenopruiming via referentietelling - in PyPy werden objecten niet onmiddellijk verwijderd en schoongemaakt, maar volgens de "wens" van de GC, wat leidde tot onvoorspelbaar gedrag met open bestanden en netwerkverbindingen.