CPython est la la machine virtuelle de référence d'exécution des programmes Python. CPython compile et interprète les sources d'une programme python pour son exécution.
A ce titre, il prend en charge la gestion de la mémoire pour les variables ou les objets Python.
Dans un but de rationnaliser l'empreinte mémoire, il va mettre en place deux mécanismes distincts pour gérer au mieux cette mémoire.
Un objet est composé de deux choses: un nom (1) de variable qui fait référence à une zone mémoire (2).
Quand un objet est détruit (commande del ?, fin de bloc ?) , le nom de l'objet (variable) est enlevé de l'espace de nommage mais la zone mémoire ne sera forcement rendue disponible. C'est à ce moment que les deux mécanismes évoqués vont entrer en jeu.
1) La gestion par compteur de référence.
Ce dispositif est à la fois simple, robuste et efficace.
Chaque fois un objet est référencé par un autre, un compteur de référence est incrémenté de 1. Le mécanisme inverse s'applique chaque fois qu'un objet est déréférencé par un autre objet. Quand le compteur arrive à 0 , l'espace mémoire occupé par l'objet se récupéré.
Remarque : une opération comme celle-ci : a = b * 4 va incrémenter de 1 le compteur de référence de la variable b puis à la fin de l'operation le décrémenter.
Exemple: utilisation de getrefcount pour afficher le nombre de référence.
import sys > a = 50000 b = ['etoile', a, 'neige'] c = (a, b) d ='srer' print('a', sys.getrefcount(a)) print('b', sys.getrefcount(b)) print('c', sys.getrefcount(c)) print('d', sys.getrefcount(d)) => a 4 b 3 c 2 d 2
On supprime un objet qui en référençait un autre
del(c) print('a', sys.getrefcount(a)) print('b', sys.getrefcount(b)) print('d', sys.getrefcount(d)) => a 3 b 2 d 2
y = ['un' , 'deux' ] z = [3, 5, 7] print('y', sys.getrefcount(y)) print('z', sys.getrefcount(z)) => y 2 z 2On ajoute à chaque liste , l'autre liste.
y.append(z) z.append(y) print('y', sys.getrefcount(y)) print('z', sys.getrefcount(z)) => y 3 z 3
On supprime la liste 'z'
del(z) print('y', sys.getrefcount(y)) print(y) => y 3 ['un', 'deux', [3, 5, 7, [...]]]Le compteur de référence sur y n'a pas bougé car même si la variable n'est plus accessible, son contenu persiste en mémoire et il pointe toujours sur y.
2) Le garbage collector de CPython.
import gc gc.get_threshold() => (700, 10, 10) gc.get_stats() => [{'collections': 370, 'collected': 32693, 'uncollectable': 0}, {'collections': 33, 'collected': 3952, 'uncollectable': 0}, {'collections': 3, 'collected': 1561, 'uncollectable': 0}]
La méthode get_stats() retourne le nombre d'objet surveillé par génération.
Pour illustration:
On va créer une référence circulaire , puis supprimer l'objet et voir ainsi le travail du garbage collector
Le garbage collector est placé en mode debug.
# Préparation gc.set_debug( gc.DEBUG_COLLECTABLE| gc.DEBUG_SAVEALL ) n = gc.collect() zorro = [1, 2,'autre chose'] maliste = ['ert', 4, 'divers'] # Création d'une référence circulaire zorro.append(zorro) # suppression de l'objet del zorro # Mesures avant = [ str(x) for x in gc.garbage] print(len(avant)) avant= set(avant) n = gc.collect() print(n) apres = [ str(x) for x in gc.garbage] print(len(apres)) apres = set(apres) dif = apres- avant print("result ", dif) => 1753 1 1754 result {"[1, 2, 'autre chose', [...]]"} gc: collectable <list 0x0000017E6AFDD480>
La zone mémoire sera libérée et sera disponible pour un autre stockage.
MAIS ce n'est pas pour autant que CPython rendra de l'espace mémoire au système d'exploitation.
Conclusion.
Il est possible de modifier le seuil de déclenchement du garbage collector (méthode set_threshold)
ou encore de le désactiver. Il n'y aucune raison a priori de modifier le comportement par défaut.
Aucun commentaire:
Enregistrer un commentaire