Cours
La gestion de la mémoire est un aspect essentiel, souvent sous-estimé, du développement. Mal maîtrisée, elle peut entraîner des applications lentes, des plantages inattendus, voire des fuites de mémoire. Heureusement, Python s’en charge via un mécanisme intégré appelé garbage collection, qui gère automatiquement la mémoire.
Mais comment fonctionne exactement la garbage collection de Python, et pourquoi est-ce important ? Comprendre ce processus est clé pour écrire un code performant et fiable, même lorsque vos projets gagnent en complexité.
À la fin de cet article, vous connaîtrez les bases de la garbage collection en Python et son importance, vous comprendrez comment Python utilise le comptage de références et la collecte générationnelle pour gérer la mémoire, et vous apprendrez quelques conseils pour éviter les fuites de mémoire. Nous vous invitons également à suivre notre cours Writing Efficient Python Code, qui vous apprendra à allouer les ressources de votre environnement et à prendre en compte les surcoûts — la garbage collection n’en étant qu’un volet.
Qu’est-ce que la garbage collection en Python ?
La garbage collection est le système automatique de gestion de la mémoire de Python. Elle garantit la fluidité de vos applications en libérant la mémoire qui n’est plus utilisée. En d’autres termes, c’est comme un agent d’entretien invisible qui nettoie derrière votre code en supprimant les objets devenus inutiles.

« Python collecting garbage. » Image par Dall-E 2
Pourquoi a-t-on besoin de garbage collection en Python ?
Dans toute application, des objets sont créés pour stocker des données, effectuer des calculs ou orchestrer des tâches. Une fois leur rôle terminé, ces objets continuent pourtant d’occuper de la mémoire tant qu’ils ne sont pas supprimés explicitement. S’ils s’accumulent, votre application peut consommer plus de mémoire que nécessaire, avec à la clé des ralentissements ou des crashs.
La garbage collection évite ces scénarios. Elle détecte automatiquement lorsqu’un objet n’est plus nécessaire (aucune partie de votre code ne le référence) et le supprime en toute sécurité de la mémoire. Ce processus permet de :
- Prévenir les fuites de mémoire : en nettoyant automatiquement les objets inutilisés, on réduit le risque que de la mémoire ne soit pas restituée.
- Optimiser les performances : en libérant la mémoire, la garbage collection aide à maintenir les performances, notamment pour les programmes longue durée ou manipulant de grands volumes de données.
- Simplifier le développement : Python gérant automatiquement la mémoire, vous pouvez vous concentrer sur l’écriture de code plutôt que sur son allocation.
Comment Python implémente automatiquement la garbage collection
La garbage collection de Python est un système sophistiqué de gestion automatique de la mémoire, qui permet aux développeurs de se concentrer sur le code. Python s’appuie principalement sur deux mécanismes : le comptage de références et la collecte générationnelle. Ensemble, ils gèrent efficacement la mémoire, limitent les fuites et optimisent les performances de vos applications.
Avant d’entrer dans le détail, précisons que ce processus concerne surtout CPython, l’implémentation la plus utilisée de Python. D’autres implémentations comme PyPy ou Jython peuvent gérer la garbage collection différemment, mais CPython s’appuie fortement sur ces deux méthodes pour assurer la stabilité de vos applications.
Comptage de références
Le comptage de références est la base de la gestion mémoire en Python. Il consiste à suivre le nombre de références (ou pointeurs) vers un objet en mémoire. À chaque nouvelle référence, Python incrémente le compteur de l’objet ; inversement, lorsqu’une référence est supprimée ou sort du scope, il le décrémente. Voici le principe :
- Suivi des références : chaque objet en Python possède un compteur de références, mis à jour lorsqu’il est référencé ou déréférencé. Par exemple, l’assigner à une variable ou le passer à une fonction augmente son compteur, tandis que la suppression de la variable le diminue.
- Désallocation de la mémoire : quand le compteur atteint zéro, c’est qu’aucune partie de votre code n’utilise l’objet ; Python libère alors automatiquement la mémoire qu’il occupait.
Malgré son efficacité, le comptage de références a des limites. La plus notable concerne les références cycliques, lorsque deux objets (ou plus) se référencent mutuellement et forment un cycle. Leurs compteurs ne tombent alors jamais à zéro, empêchant la récupération de la mémoire. C’est là que la collecte générationnelle intervient.
Collecte générationnelle
Pour pallier ces limites, Python emploie aussi une collecte générationnelle. Cette méthode avancée gère les références cycliques et améliore l’efficacité globale. L’idée : la plupart des objets sont soit de courte durée (temporaires), soit de longue durée (persistants). En classant les objets par « âge », Python optimise la collecte. Voici comment elle fonctionne :
-
Générations : le collecteur répartit les objets en trois générations : génération 0 (la plus jeune), génération 1 (intermédiaire) et génération 2 (la plus ancienne). Les nouveaux objets sont placés en génération 0 ; s’ils « survivent » à une collecte, ils passent à la génération suivante.
-
Priorité aux objets jeunes : la collecte s’exécute plus souvent sur les objets jeunes (génération 0), plus susceptibles d’être rapidement inutiles. En vieillissant, les objets sont collectés moins fréquemment, ce qui réduit la surcharge en se concentrant là où l’impact est le plus fort.
-
Gestion des cycles : la collecte générationnelle identifie et récupère efficacement les objets impliqués dans des références cycliques. Durant la collecte, Python détecte ces cycles et reprend la mémoire, évitant les fuites liées aux cycles.
Comment déclencher manuellement la garbage collection en Python
Même si le système de garbage collection est automatique, certaines situations gagnent à être gérées manuellement. Voyons quelques cas.
Déclencher une collecte en Python
La plupart du temps, le collecteur s’exécute sans intervention. Mais vous pouvez vouloir forcer une collecte pour libérer de la mémoire, notamment dans des applications longue durée ou des traitements gourmands où l’empreinte mémoire doit être maîtrisée de près.
Pour déclencher manuellement la collecte, utilisez la fonction gc.collect(). Elle force l’exécution immédiate du collecteur et récupère la mémoire inutilisée. Fonctionnement :
import gc
# Trigger garbage collection manually
gc.collect()
Lorsque vous appelez gc.collect(), Python effectue une collecte complète : il examine tous les objets en mémoire et désalloue ceux qui ne sont plus référencés. C’est utile notamment pour :
- Applications très consommatrices : si vous traitez de gros volumes de données ou créez de nombreux objets, déclencher la collecte à des moments clés peut limiter l’enflement mémoire.
- Processus longue durée : pour des services ou tâches en continu (serveurs, jobs en arrière-plan), une gestion manuelle ponctuelle aide à garder une empreinte mémoire stable et l’application réactive.
Cependant, utilisez gc.collect() avec discernement. Des appels trop fréquents ajoutent de la surcharge, car la collecte est coûteuse. Réservez-la aux cas où vous avez identifié un problème mémoire ou devez libérer précisément à un instant donné.
Désactiver la garbage collection
Dans certains cas, il peut être pertinent de désactiver temporairement la collecte automatique — par exemple dans des applications temps réel, des sections critiques en performance, ou quand vous souhaitez éviter toute interruption.
Pour la désactiver, utilisez gc.disable() :
import gc
# Disable automatic garbage collection
gc.disable()
Une fois désactivée, Python n’exécutera plus la collecte automatique. Cela peut apporter une performance plus prévisible dans certaines situations, en évitant des déclenchements intempestifs durant des opérations critiques.
Mais cette pratique comporte des risques :
- Fuites de mémoire : sans collecte, les objets inutilisés restent en mémoire jusqu’à la fin du processus, ce qui peut poser problème pour les applications longue durée.
- Références cycliques : comme les cycles ne sont pas résolus automatiquement si la collecte est désactivée, les soucis mémoire peuvent s’aggraver en présence de cycles.
Il est donc essentiel de la réactiver après la section critique. Utilisez gc.enable() :
# Re-enable automatic garbage collection
gc.enable()
Voici quelques bonnes pratiques pour la gestion manuelle :
- À utiliser avec parcimonie : la gestion manuelle ne doit être appliquée qu’en cas de besoin. En abuser peut nuire aux performances.
- Associer à du profilage : avant de déclencher ou désactiver la collecte, profilez votre application pour comprendre ses schémas d’usage mémoire. Vous saurez ainsi où intervenir avec le plus d’impact.
- Réactiver au bon moment : si vous désactivez la collecte, pensez à la réactiver dès que la section critique est terminée pour retrouver une gestion mémoire efficace sur la durée.
En comprenant comment et quand intervenir manuellement, vous gagnez un contrôle plus fin sur la mémoire de vos applications : à la clé, des performances optimisées et moins de problèmes de stabilité liés à la mémoire.
Dans la section suivante, nous passerons en revue des problèmes courants de garbage collection en Python et des conseils pratiques pour les diagnostiquer et les corriger.
Points d’attention pour les développeurs Python
Une gestion efficace de la mémoire est cruciale pour écrire des applications Python performantes. Même si la garbage collection couvre l’essentiel, vous pouvez adopter des pratiques concrètes pour éviter les écueils et optimiser l’usage mémoire. Examinons des stratégies pour éviter les fuites, gérer les références cycliques et manipuler un grand nombre d’objets.
Éviter les fuites de mémoire
Les fuites surviennent lorsque des objets devenus inutiles ne sont pas correctement désalloués, faisant croître la consommation mémoire au fil du temps. Cela peut dégrader les performances voire provoquer un crash. Pour surveiller les objets en mémoire, utilisez le module gc. En observant le nombre d’objets suivis, vous pouvez détecter des augmentations inattendues, signe possible d’une fuite :
import gc
# Get a list of all objects tracked by the garbage collector
all_objects = gc.get_objects()
print(f"Number of tracked objects: {len(all_objects)}")
Si vous suspectez qu’un objet n’est pas collecté, la fonction gc.get_referrers() aide à identifier ce qui le maintient en mémoire. Elle retourne la liste des objets qui le référencent, pour déterminer si ces références sont nécessaires ou peuvent être supprimées :
some_object = ...
referrers = gc.get_referrers(some_object)
print(f"Object is being referenced by: {referrers}")
Autre approche : utiliser des références faibles, notamment pour des mécanismes de cache. Le module weakref permet de créer des références qui n’augmentent pas le compteur de références de l’objet :
import weakref
class MyClass:
pass
obj = MyClass()
weak_obj = weakref.ref(obj)
print(weak_obj()) # Access the object
del obj # Delete the strong reference
print(weak_obj()) # None, as the object is now collected
Gérer les références cycliques
Les cycles apparaissent quand deux objets (ou plus) se référencent mutuellement, empêchant leurs compteurs de tomber à zéro. Cela peut conduire à des fuites si le collecteur ne parvient pas à les éliminer.
La collecte générationnelle de Python gère ces cycles, mais le mieux reste d’éviter d’en créer inutilement. Avec le module gc, vous pouvez détecter des objets impliqués dans des cycles en déclenchant une collecte et en inspectant la liste gc.garbage, qui contient les objets non collectables détectés dans des cycles :
import gc
# Trigger garbage collection and get the number of uncollectable objects
gc.collect()
uncollectable_objects = gc.garbage
print(f"Number of uncollectable objects: {len(uncollectable_objects)}")
Pour prévenir les cycles, pensez à rompre explicitement les références lorsqu’elles ne sont plus nécessaires (en les mettant à None) ou utilisez des références faibles, très utiles pour éviter des cycles forts. Simplifier la conception de vos structures de données et relations d’objets diminue aussi le risque de cycles.
Gérer un grand nombre d’objets
Créer et détruire un très grand nombre d’objets sur un laps de temps réduit peut solliciter fortement le collecteur. C’est le cas d’applications qui traitent de gros jeux de données, des flux temps réel ou des opérations complexes.
Une idée consiste à regrouper les créations et destructions d’objets par lots. Au lieu de créer/supprimer un par un, procédez par batchs pour réduire la fréquence de collecte et améliorer son efficacité. Par exemple :
objects = []
for i in range(1000):
obj = SomeClass()
objects.append(obj)
# Process all objects at once, then delete them
del objects[:]
gc.collect() # Optionally trigger a manual garbage collection
Autre point : optimiser la durée de vie des objets, en veillant à ce qu’ils soient clairement de courte ou de longue durée. Les objets éphémères sont vite collectés ; les objets durables montent en génération et sont collectés moins souvent.
Lorsque des objets sont fréquemment créés et détruits, un pool d’objets peut être utile. Il réutilise un nombre fixe d’instances, réduisant la pression sur le collecteur et améliorant les performances en environnement contraint. Exemple :
class ObjectPool:
def __init__(self, size):
self.pool = [SomeClass() for _ in range(size)]
def get(self):
return self.pool.pop()
def release(self, obj):
self.pool.append(obj)
Les pools sont particulièrement intéressants pour des applications temps réel ou des jeux, où la performance est critique et où la création/destruction fréquente d’objets coûte cher.
Conclusion
Dans cet article, nous avons parcouru le système de garbage collection de Python, de sa gestion automatique aux interventions manuelles possibles pour améliorer les performances. L’association du comptage de références et de la collecte générationnelle permet à Python de gérer efficacement la mémoire, même si, dans certains cas, une intervention manuelle est pertinente. En appliquant les bonnes pratiques présentées, vous éviterez les fuites, optimiserez les performances et reprendrez la main sur la gestion mémoire de vos applications.
Pour aller plus loin sur la gestion mémoire et l’optimisation globale du code, explorez nos ressources DataCamp : les tutoriels How to Write Memory-Efficient Classes in Python et Memory Profiling in Python, utiles pour repérer les goulots d’étranglement. Par ailleurs, apprendre à écrire un code Python plus efficace est un atout pour toute carrière.
Devenez ingénieur en données
Professionnel expérimenté des données et écrivain passionné par l'autonomisation des experts en herbe dans le domaine des données.
FAQ sur la garbage collection en Python
Comment puis-je surveiller l’utilisation mémoire dans des applications Python ?
Avec des outils comme memory_profiler, tracemalloc et objgraph. Ils permettent de suivre la consommation mémoire, d’identifier des fuites et d’optimiser l’usage de la mémoire.
Comment déclencher manuellement la garbage collection en Python ?
En utilisant le module gc. Des fonctions comme gc.collect() permettent de forcer un cycle de collecte, utile pour le débogage ou l’optimisation mémoire dans des cas précis.
Peut-on désactiver la garbage collection de Python ?
Oui, vous pouvez désactiver la garbage collection avec le module gc. En appelant gc.disable(), vous empêchez le collecteur de s’exécuter automatiquement.
