ProgrammationDéveloppeur Backend

Expliquez comment fonctionne la gestion de la mémoire en Python, y compris le ramasse-miettes. Qu'est-ce que les références cycliques et comment Python les gère-t-il ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

En Python, la mémoire est gérée automatiquement : lorsqu'un objet est créé, de la mémoire est allouée, et lorsque nenhum autre objet ne fait référence à cet objet, la mémoire est libérée. La stratégie principale est le compte des références : chaque objet est lié à un compteur de références, qui, lorsqu'il atteint zéro, l'objet est supprimé.

Cependant, des références cycliques peuvent se produire lorsque des objets se réfèrent les uns aux autres, et leur compteur de références ne tombe jamais à zéro, même si des références du code externe sont supprimées.

Pour lutter contre cela, Python (CPython) a un ramasse-miettes (garbage collector, GC) qui détecte les cycles d'objets inaccessibles à partir des racines et les supprime. Le ramasse-miettes peut être géré à partir du module gc.

Exemple de cycle :

class Node: def __init__(self): self.ref = None n1 = Node() n2 = Node() n1.ref = n2 n2.ref = n1 del n1, n2 # les objets sont toujours vivants à cause de la référence cyclique !

Question piégée

Question : « Si vous appelez del obj, la mémoire sera toujours libérée immédiatement. Est-ce vrai ? »

Réponse : Non ! L'opération del ne fait que supprimer une référence à l'objet. S'il reste d'autres références (explicites ou cycliques), la mémoire ne sera pas libérée tant que le compteur de références ne sera pas égal à zéro et/ou que le GC ne se débarrassera pas des cycles. Exemple :

import gc class A: pass x = A() y = x del x # y a toujours une référence, l'objet est vivant ! del y # seulement maintenant l'objet peut être supprimé

Exemples d'erreurs réelles dues à une méconnaissance des subtilités du sujet


Histoire

Dans un système de traitement de gros fichiers, des objets liés étaient maintenus en mémoire. Après la suppression des références externes, la mémoire n'était toujours pas libérée. La raison — des références cycliques qui n'étaient pas immédiatement nettoyées, et parfois du fait d'un destructeur __del__.


Histoire

Dans un processus serveur à long terme, de grandes structures temporaires avec des graphes cycliques étaient souvent créées. Les développeurs n'avaient pas désactivé la sortie temporaire de débogage du GC, ce qui a entraîné des pauses rares mais perceptibles en arrière-plan. Diagnostiquées comme des "baisses de performance". Il s'est avéré que la cause était le lancement fréquent du ramasse-miettes.


Histoire

Lors du portage d'un grand projet sur PyPy (interpréteur alternatif), l'ancien code s'appuyait sur le comportement spécifique de CPython en matière de nettoyage immédiat de la mémoire via le compteur de références — dans PyPy, les objets n'étaient pas supprimés et nettoyés immédiatement, mais selon le "désir" du GC, ce qui a conduit à un comportement imprévisible avec des fichiers ouverts et des connexions réseau.