La gestion de la mémoire est le processus de gestion efficace de la mémoire informatique (RAM).Cela implique d'allorer un morceau de mémoire au moment de l'exécution au programme lorsque le programme le demande et la libération de la mémoire allouée pour être réutilisée lorsque le programme n'en a plus besoin.
Dans des langages comme C ou Rust, la gestion de la mémoire est la responsabilité du programmeur.Le programmeur doit allouer manuellement la mémoire avant de pouvoir être utilisé par le programme et le publier lorsque le programme n'en a plus besoin.Dans Python, la gestion de la mémoire est automatique!Python gère automatiquement l'allocation et le traitement de la mémoire.
Dans cet article, nous discuterons des internes de la gestion de la mémoire dans Python.Nous couvrirons également comment les unités fondamentales, telles que les objets, sont stockées en mémoire, différents types d'allocateurs de mémoire dans Python et comment le gestionnaire de mémoire de Python gère efficacement la mémoire.
Comprendre les internes de la gestion de la mémoire dans Python aide à concevoir des applications économes en mémoire.Il facilite également le débogage des problèmes de mémoire dans une application.
Table des matières
- Python comme spécification de langue
- Qu'est-ce que Cpython
- Que sont les fonctions malloc et libres en C?
- Objets en python
- Variables en python
- Gestion de la mémoire dans CPYTHON
- Allocateurs de mémoire
- Allocateur d'objet
- Blocs
- Piscines
- Arènes
- Un processus Python publie-t-il la mémoire?
- Collection des ordures à Python
- Décompte de référence
- Collection des ordures sur la base du nombre de références
- Références cycliques à Python
- Collection de déchets générationnels
- Module GC dans Python
Commençons par comprendre Python comme spécification de langue, puis plongez profondément dans cpython!
Python comme spécification de langue
Un langage de programmation est un ensemble de règles et de spécifications, tels que définis dans un document de référence.
Python est un langage de programmation, etceest le document de référence pour Python qui indique les règles et spécifications pour la langue Python.
Par exemple, la spécification du langage Python indique que pour définir une fonction, nous devons utiliser ledef
mot-clé.Ce n'est qu'une spécification, et nous devons faire comprendre à l'ordinateur qu'en utilisantdef function_name
, nous avons l'intention de définir une fonction avec le nomfunction_name
.Nous devons rédiger un programme qui met en œuvre ces règles et spécifications.
Les règles et spécifications du langage Python sont implémentées par divers langages de programmation, tels que C, Java et C #.L'implémentation de la langue python en C est appeléeCpython, tandis que l'implémentation de la langue python en java et c # est appeléeConfitureetIronpython, respectivement.
Qu'est-ce que Cpython?
CPython est l'implémentation par défaut et la plus utilisée de la langue Python.Lorsque nous disons Python, cela signifie essentiellement que nous faisons référence à Cpython.Lorsque vous téléchargez Python depuispython.org, vous téléchargez essentiellement le code cpython.Ainsi, Cpython est un programme écrit en langue C qui met en œuvre toutes les règles et spécifications définies par la langue Python.
CPython est la mise en œuvre de référence du langage de programmation Python.CPython peut être défini à la fois comme un interprète et un compilateur, car il compile le code Python en bytecode avant de l'interpréter.-Wikipédia
Étant donné que CPython est l'implémentation de référence, toutes les nouvelles règles et spécifications du langage Python sont d'abord implémentées par CPYthon.
Dans cet article, nous discuterons des internes de gestion de la mémoire de CPYTHON.
Veuillez noter: d'autres implémentations, telles queConfitureetIronpython, peut implémenter la gestion de la mémoire d'une manière différente.
Comme Cpython est implémenté dans le langage de programmation C, comprenons d'abord deux fonctions importantes liées à la gestion de la mémoire en C:Malloc
etgratuit
!
Que sontMalloc
etgratuit
Fonctions en C?
D'abord,Malloc
est une méthode utilisée dans le langage de programmation C pour demander un bloc de mémoire du système d'exploitation au moment de l'exécution.Lorsqu'un programme a besoin de mémoire au moment de l'exécution, il appelle leMalloc
Méthode pour obtenir la mémoire requise.
Deuxième,gratuit
est une méthode utilisée dans le langage de programmation C pour libérer ou libérer la mémoire allouée au programme au système d'exploitation lorsque le programme n'en a plus besoin.
Lorsqu'un programme Python (CPython) a besoin de mémoire, CPython appelle en interne leMalloc
Méthode pour l'allouer.Lorsque le programme n'a plus besoin de la mémoire, Cpython appelle legratuit
Méthode pour le libérer.
Ensuite, voyons comment la mémoire est allouée à différents objets dans Python!
Objets en python
Tout dans Python est un objet.Des classes,les fonctions, et même des types de données simples, tels queentiers,chars, etcordes, sont des objets en python.Lorsque nous définissons un entier dans Python, Cpython crée en interne un objet de typeentier.Ces objets sont stockés dans la mémoire du tas.
Chaque objet Python se compose de trois champs:
- Valeur
- Taper
- Décompte de référence
Prenons un exemple simple:
un = 100
Lorsque le code ci-dessus est exécuté, Cpython crée un objet de typeentier
et alloue la mémoire à cet objet sur la mémoire du tas.
Letaper
indique le type de l'objet dans cpython et levaleur
Le champ, comme son nom l'indique, stocke la valeur de l'objet (100
dans ce cas).Nous discuterons duref_count
champ plus tard dans l'article.
Variables en python
Les variables de Python ne sont que des références à l'objet réel en mémoire.Ils sont comme des noms ou des étiquettes qui pointent vers l'objet réel en mémoire.Ils ne stockent aucune valeur.
Considérez l'exemple suivant:
un = 100
Comme indiqué précédemment, lorsque le code ci-dessus est exécuté, CPython crée en interne un objet de typeentier.La variableun
pointe vers cet objet entier comme indiqué ci-dessous:
Nous pouvons accéder à l'objet entier dans le programme Python en utilisant la variableun
.
Attribuons cet objet entier à une autre variableb
:
b = un
Lorsque le code ci-dessus est exécuté, les variablesun
etb
Les deux pointent vers le même objet entier, comme indiqué ci-dessous:
Incrémentons maintenant la valeur de l'objet entier de 1:
# Incrément A par 1un = un + 1
Lorsque le code ci-dessus est exécuté, CPython crée un nouvel objet entier avec la valeur101
et rend variableun
pointez ce nouvel objet entier.Variableb
continuera à pointer vers l'objet entier avec la valeur100
, comme indiqué ci-dessous:
Ici, nous pouvons voir qu'au lieu d'écraser la valeur de100
avec101
, Cpython crée un nouvel objet avec la valeur101
Parce que les entiers en python sont immuables.Une fois créé, ils ne peuvent pas être modifiés.Veuillez noter que les flotteurs et les types de données de chaîne sont également immuables dans Python.
Voyons un simple programme Python pour expliquer davantage ce concept:
je = 0alors que je < 100: je = je + 1
Le code ci-dessus définit un simplealors que
boucle qui augmente la valeur de la variableje
Jusqu'à ce qu'il soit inférieur100
.Lorsque ce code est exécuté, pour chaque incrément de la variableje
, Cpython créera un nouvel objet entier avec la valeur incrémentée, et l'ancien objet entier sera supprimé (pour être plus précis, cet objet deviendrait éligible à la suppression) de la mémoire.
Cpython appelle leMalloc
Méthode pour chaque nouvel objet pour allouer la mémoire à cet objet.Il appelle legratuit
Méthode pour supprimer l'ancien objet de la mémoire.
Converons le code ci-dessus en termes deMalloc
etgratuit
:
je = 0 # malloc (i)alors que je < 100: # malloc (i + 1) # gratuit (i) je = je + 1
Nous pouvons voir que Cpython crée et supprime un grand nombre d'objets, même pour ce programme simple.Si nous appelons leMalloc
etgratuit
Méthodes pour chaque création et suppression d'objets, il dégradera les performances d'exécution du programme et rendra le programme lent.
Par conséquent, Cpython introduit diverses techniques pour réduire le nombre de fois que nous devons appelerMalloc
etgratuit
pour chaque petite création d'objets et suppression.Comprenons maintenant comment Cpython gère la mémoire!
Gestion de la mémoire dans CPYTHON
La gestion de la mémoire à Python implique la gestion d'un tas privé.Un tas privé est une partie de la mémoire exclusive au processus Python.Tous les objets Python et structures de données sont stockés dans le tas privé.
Le système d'exploitation ne peut pas allouer ce morceau de mémoire à un autre processus.La taille du tas privé peut se développer et rétrécir en fonction des exigences de mémoire du processus Python.Le tas privé est géré par le Python Memory Manager défini à l'intérieur du code CPYthon.
À des fins de représentation, le tas privé dans CPYthon peut être divisé en plusieurs parties comme indiqué ci-dessous:
Veuillez noter que les limites de chacune de ces parties ne sont pas fixées et peuvent croître ou rétrécir en fonction de l'exigence.
- Mémoire non object de noyau Python: Partie de la mémoire allouée aux données non-objet Python Core.
- Tampons internes: Partie de la mémoire allouée aux tampons internes.
- Mémoire spécifique à l'objet- partie de la mémoire allouée à des objets qui ont des allocateurs de mémoire spécifiques aux objets.
- Mémoire de l'objet: Partie de la mémoire allouée aux objets.
Lorsque le programme demande de la mémoire, CPython utilise leMalloc
Méthode pour demander cette mémoire dans le système d'exploitation et le tas privé augmente en taille.
Pour éviter d'appelerMalloc
etgratuit
Pour chaque création et suppression de petits objets, Cpython définit plusieurs allocateurs et transacteurs à différentes fins.Nous en discuterons en détail chacun dans la section suivante!
Allocateurs de mémoire
Pour éviter d'appeler leMalloc
etgratuit
Méthodes fréquemment, Cpython définit une hiérarchie d'allocateurs, comme indiqué ci-dessous:
Voici la hiérarchie des allocateurs de mémoire du niveau de base:
- Allocateur à usage général (Cpython's
Malloc
méthode) - Allocateur de mémoire brute (pour des objets supérieurs à 512 octets)
- Allocator d'objets (pour les objets plus petits ou égaux à 512 octets)
- Allocateurs spécifiques à l'objet (allocateurs de mémoire spécifiques pour des types de données spécifiques)
Au niveau de la base est leà usage général
allocateur.Leà usage général
L'allocateur est leMalloc
Méthode du langage C pour cpython.Il est responsable de l'interaction avec le gestionnaire de mémoire virtuelle du système d'exploitation et de l'allocation de la mémoire requise au processus Python.C'est le seul allocateur qui communique avec le gestionnaire de mémoire virtuel du système d'exploitation.
En haut duà usage général
L'allocateur est Pythonmémoire brute
allocateur.Lemémoire brute
L'allocateur fournit une abstraction auà usage général
allocateur (c'est-à-dire leMalloc
méthode).Lorsqu'un processus Python a besoin de mémoire, lemémoire brute
L'allocateur interagit avec leà usage général
allocateur pour fournir la mémoire requise.Il s'assure qu'il y a suffisamment de mémoire pour stocker toutes les données du processus Python.
Au-dessus de lamémoire brute
allocateur, nous avons un allocateur d'objet.Cet allocateur est utilisé pour allouer la mémoire pour les petits objets (plus petit ou égal à 512 octets).Si un objet a besoin de plus de 512 octets de mémoire, le gestionnaire de mémoire de Python appelle directement lemémoire brute
allocateur.
Comme le montre la représentation ci-dessus, nous avons des allocateurs spécifiques à l'objet en plus de l'allocateur d'objet.Des types de données simples, commeentier,flotter,chaîne, etliste, ont des allocateurs respectifs spécifiques aux objets.Ces allocateurs spécifiques à l'objet mettent en œuvre des politiques de gestion de la mémoire selon l'exigence de l'objet.Par exemple, l'allocateur spécifique à l'objet pourentiera une implémentation différente de l'allocateur spécifique à l'objet pourflotter.
Les allocateurs et les allocateurs d'objets spécifiques à l'objet fonctionnent sur la mémoire déjà alloués au processus Python par l'allocateur de mémoire brute.Ces allocateurs ne demandent jamais de mémoire du système d'exploitation.Ils opèrent sur le tas privé.Si l'allocateur d'objet ou l'allocateur spécifique à l'objet a besoin de plus de mémoire, l'allocateur de mémoire brute de Python le fournit en interagissant avec l'allocateur à usage général.
Hiérarchie des allocateurs de mémoire à Python
Lorsqu'un objet demande une mémoire et que l'objet a des allocateurs spécifiques à l'objet définis, les allocateurs spécifiques à l'objet sont utilisés pour allouer la mémoire.
Si l'objet n'a pas d'allocateurs spécifiques à l'objet et que plus de 512 octets de mémoire sont demandés, le Python Memory Manager appelle directement l'allocateur de mémoire brute pour allouer la mémoire.
Si la taille de la mémoire demandée est inférieure à 512 octets, les allocateurs d'objets sont utilisés pour l'allouer.
Allocateur d'objet
L'allocateur d'objet est également appelépymalloc
.Il est utilisé pour allouer la mémoire aux petits objets de moins de 512 octets.
Le cpythonbase de codedécrit l'allocateur d'objet comme
Un allocateur de mémoire rapide et à usage spécial pour les petits blocs, à utiliser sur un Malloc à usage général.
Il est appelé à chaque allocation et de négociation d'objets (pyObject_new / del) à moins que les allocateurs spécifiques à l'objet implémentent un schéma d'allocation propriétaire (Ex.: INTS utilise une liste gratuite simple).
C'est également l'endroit où le collecteur de déchets cycliques fonctionne sélectivement sur les objets de conteneur.
Lorsqu'un petit objet demande de la mémoire, au lieu d'allorer simplement la mémoire pour cet objet, l'allocateur d'objet demande un gros bloc de mémoire du système de fonctionnement.Ce grand bloc de mémoire est ensuite utilisé plus tard pour allouer la mémoire à d'autres petits objets.
De cette façon, l'allocateur d'objet évite d'appelerMalloc
pour chaque petit objet.
Le grand bloc de mémoire que l'allocateur d'objet alloue s'appelle unArène
.Les arènes ont une taille de 256 Ko.
UtiliserArènes
efficacement, Cpython diviseArène
dansPiscines
.Pools sont 4 Ko.Alors, unarène
CAN se compose de 64 piscines (256 Ko / 4Kb).
Les piscines sont en outre divisées enBlocs
.
Ensuite, nous discuterons de chacun de ces composants!
Blocs
Les blocs sont la plus petite unité de mémoire que l'allocateur d'objet peut allouer à un objet.Un bloc peut être alloué à un seul objet, et un objet ne peut être alloué qu'à un bloc.Il est impossible de placer des parties d'un objet en deux blocs séparés ou plus.
Les blocs sont disponibles en différentes tailles.La plus petite taille d'un bloc est de 8 octets, tandis que la taille maximale d'un bloc est de 512 octets.La taille d'un bloc est un multiple de 8, et par conséquent, les tailles de blocs peuvent être de 8, 16, 32, ..., 504 ou 512 octets.Chacune des tailles de blocs est appelée unClasse de taille.Il y a 64 classes de taille, comme indiqué ci-dessous:
Comme le montre le tableau ci-dessus, les blocs de classe 0 de taille ont une taille de 8 octets, tandis que les blocs de taille de classe 1 ont une taille de 16 octets, etc.
Les programmes sont toujours alloués d'un bloc complet ou pas de blocs.Ainsi, si un programme demande 14 octets de mémoire, il a alloué un bloc de 16 octets.De même, si un programme demande 35 octets de mémoire, un bloc de 40 octets est alloué.
Piscines
Une piscine se compose de blocs d'une seule classe de taille.Par exemple, un pool qui a un bloc de taille 0 ne peut pas avoir de blocs d'une autre classe de taille.
La taille du pool est égale à la taille de la page de mémoire virtuelle.Ici `` Ce que le terme page de mémoire virtuelle signifie:
Une page, une page de mémoire ou une page virtuelle est un bloc contigu de la longueur de mémoire virtuelle.Il s'agit de la plus petite unité de données pour la gestion de la mémoire dans un système d'exploitation de mémoire virtuelle.-Wikipédia
Dans la plupart des cas, la taille de la piscine est de 4 Ko.
Les piscines ne sont sculptées que dans les arènes lorsqu'il n'y a pas d'autre pool disponible qui a un bloc de la classe de taille demandée.
Une piscine peut être dans l'un des trois états:
Utilisé: Une piscine est censée être dans le
utilisé
État s'il a des blocs disponibles pour l'allocation.Complet: Une piscine est censée être dans le
complet
État si tous les blocs de la piscine sont alloués.VideUne piscine serait dans le
vide
État si tous les blocs de la piscine sont disponibles pour l'allocation.Une piscine vide n'a pas de classe de taille qui lui est associée.Il peut être utilisé pour attribuer des blocs de n'importe quelle classe de taille.
Les pools sont définis dans le code cpython comme indiqué ci-dessous:
/ * Pool pour les petits blocs.* /structure Pool_header { syndicat { bloc *_rembourrage; uint compter; } référence; / * Nombre de blocs alloués * / bloc *bloc libre; / * tête de liste gratuite de la piscine * / structure Pool_header *Nextpool; / * Pool suivant de cette classe de taille * / structure Pool_header *prevpool; / * Pool précédent "" * / uint arenaindex; / * Index dans les arènes de la base ADR * / uint sidx; / * Bloquer Index de classe de taille * / uint Nextoffset; / * octets à Virgin Block * / uint maxnextoffset; / * le plus grand NIFTOFFSET VALIDE * /};
Le termesidx
Indique la classe de taille de la piscine.Sisidx
est 0 pour une piscine, il n'aura que des blocs de taille de taille 0 (c'est-à-dire des blocs de 8 octets).
Le termearenaindex
Indique l'arène à laquelle appartient la piscine.
Les pools de la même classe sont liés les uns aux autres à l'aide d'une liste doublement liée.LeNextpool
pointeur pointe vers le pool suivant de la même classe de taille, tandis que leprevpool
Le pointeur pointe vers le pool précédent de la même classe de taille.
Voici comment les piscines de la même classe sont connectées:
Lebloc libre
Le pointeur pointe vers le début d'une liste liée individuellement de blocs gratuits dans la piscine.Lorsqu'un bloc alloué est libéré, il est inséré à l'avant dubloc libre
aiguille.
Comme on le voit dans la figure ci-dessus,alloué
Les blocs sont alloués aux objets, tandis quegratuit
Les blocs ont été alloués aux objets mais sont maintenant gratuits et peuvent être alloués à de nouveaux objets.
Lorsqu'une demande est faite pour la mémoire et qu'il n'y a pas de pools avec un bloc de la classe de taille demandée disponible, CPYthon sculpte une nouvelle piscine dans l'arène.
Lorsqu'une nouvelle piscine est sculptée, la piscine entière n'est pas immédiatement fragmentée en blocs.Les blocs sont sculptés de la piscine au besoin.LeBleu
La région ombrée de la piscine dans la figure ci-dessus indique que ces parties de la piscine n'ont pas été fragmentées en blocs.
Un extrait duBase de code cpythonmentionne la sculpture des blocs de la piscine comme suit:
Les blocs disponibles dans une piscine ne sont pas liés tous ensemble lorsqu'un pool est initialisé.Au lieu de cela, seuls les blocs "les deux premiers" (adresses les plus basses) sont configurés, renvoyant le premier bloc de ce type et définissant le pool-> Freeblock sur une liste d'un bloc tenant le deuxième bloc de ce type.Cela est cohérent avec le fait que Pymalloc s'efforce à tous les niveaux (arène, piscine et bloc) pour ne jamais toucher un morceau de mémoire jusqu'à ce qu'il soit réellement nécessaire.
Ici, nous pouvons voir que lorsqu'une nouvelle piscine est sculptée dans une arène, seuls les deux premiers blocs sont sculptés dans la piscine.Un bloc est alloué à l'objet qui a demandé la mémoire, tandis que l'autre bloc est libre ou intact, et lebloc libre
Le pointeur pointe vers ce bloc.
Cpython maintient un tableau appeléusagepools
pour garder une trace des piscines dans leutilisé
État (pools disponibles pour les allocations) de toutes les classes de taille.
L'indice duusagepools
Le tableau est égal à la classe de taille de la piscine.Pour chaque indexje
de lausagepools
tableau,usagePools [i]
pointe vers l'en-tête des piscines de classe de tailleje
.Par exemple,usagePools [0]
pointe vers l'en-tête des piscines de classe de taille0
, etUskingPools [1]
pointe vers l'en-tête des piscines de classe de taille1
.
La figure ci-dessous devrait le rendre plus facile à comprendre:
Comme les pools de la même classe de taille sont liés les uns aux autres à l'aide d'une liste doublement liée, tous les pools de lautilisé
l'état de chaque classe de taille peut être traversé en utilisant leusagepools
tableau.
SiusagePools [i]
pointe versnul
, cela signifie qu'il n'y a pas de piscines de classe de tailleje
dans leutilisé
État.Si un objet demande un bloc de classe de tailleje
, Cpython taillera un nouveau pool de cours de tailleje
et mettre à jourusagePools [i]
pour pointer vers cette nouvelle piscine.
Si un bloc est libéré d'une piscinecomplet
État, l'état de la piscine passe decomplet
àutilisé
.CPython ajoute cette piscine à l'avant de la liste doublement liée des pools de sa classe de taille.
Comme on le voit dans l'image ci-dessus,pool
est d'une classe de taille0
, et c'est dans lecomplet
État.Quand un bloc est libéré depool
, son état change decomplet
àutilisé
.Une fois l'état depool
devientutilisé
, Cpython ajoute cette piscine à l'avant de la liste doublement liée des pools de classe de taille0
, etusagePools [0]
commencera à pointer vers cette piscine.
Arènes
Les arènes sont de grands blocs de mémoire utilisés pour allouer la mémoire aux petits objets.Ils sont alloués par l'allocateur de mémoire brute en tailles de 256 Ko.
Lorsqu'un petit objet demande de la mémoire, mais il n'y a pas d'arènes existantes pour gérer cette demande, au lieu de ne demander que de la mémoire pour ce petit objet, l'allocateur de mémoire brute demande un gros bloc de mémoire (256 Ko) du système d'exploitation.Ces gros blocs de mémoire sont appelés arènes.
Les piscines (de 4 kb) sont sculptées dans les arènes en cas de besoin.
Prendre en compteArena_Object
, tel que défini dans le cpythonbase de code:
structure Arena_Object { / * L'adresse de l'arène, comme retourné par Malloc * / uintptr_t adresse; / * Pointeur aligné par la piscine vers la piscine suivante à sculpter.* / bloc* pool_address; / * Le nombre de piscines disponibles dans l'arène: piscines gratuites + pools jamais alloués.* / uint nfrepools; / * Le nombre total de pools dans l'arène, qu'il soit disponible ou non.* / uint ntotalpools; / * Liste unique des piscines disponibles.* / structure Pool_header* freepools; structure Arena_Object* Nextarena; structure Arena_Object* triché;};
Lefreepools
Le pointeur pointe vers la liste des pools gratuits, et les piscines gratuites n'ont aucun de leurs blocs alloués.
Lenfrepools
Le terme indique le nombre de piscines libres dans les arènes.
CPython maintient une liste doublement liée appeléeusable_arenas
pour garder une trace de toutes les arènes avecdisponible
piscines.
Disponible
les piscines sont dans levide
ouutilisé
État.
LeNextarena
Le pointeur pointe vers l'arène utile suivante, tandis que letriché
Le pointeur pointe vers l'arène utilisable précédente dans leusable_arenas
Liste doublement liée.La liste doublement liée est triée dans l'ordre croissant dunfrepools
valeur.
Comme montré ci-dessus,usable_arenas
est trié sur la base denfrepools
.Les arènes avec 0 piscines gratuites sont le premier élément, suivi des arènes avec 1 piscine gratuite, et ainsi de suite.Cela signifie que la liste est triée avec les arènes les plus allouées en premier.Nous expliquerons pourquoi ce tri est importé dans la section suivante de l'article.
La liste des arènes utilisables est triée en fonction de celles qui ont le plus d'allocations, donc lorsqu'il y a une demande d'allocation de mémoire, il est servi à partir de l'arène avec le plus d'allocations.
Un processus Python publie-t-il la mémoire?
Lorsqu'un bloc alloué dans un pool est libéré, Cpython ne renvoie pas la mémoire au système d'exploitation.Cette mémoire continue d'appartenir au processus Python et CPython utilise ce bloc pour allouer la mémoire à de nouveaux objets.
Même lorsque tous les blocs dans un pool sont libérés, Cpython ne renvoie aucune mémoire du pool vers le système d'exploitation.Cpython garde la mémoire de l'ensemble du pool réservé à son propre usage.
Python relâche la mémoire au système d'exploitation au niveau des arènes, pas au niveau du bloc ou du pool.Veuillez également noter que Cython libère la mémoire des arènes entières à la fois.
Comme la mémoire ne peut être publiée qu'au niveau de l'arène, Cpython crée Arena à partir de la nouvelle mémoire que lorsqu'il est absolument nécessaire!Il essaie toujours d'allouer la mémoire à partir de blocs et de piscines précédemment sculptés.
C'est la raisonusable_arenas
sont triés par ordre décroissant denfrepools
.La prochaine demande de mémoire serait allouée à partir des arènes avec le plus d'allocations.Cela permet aux arènes avec les moindres données de devenir libres si les objets qu'ils contiennent sont supprimés, et la mémoire occupée par ces arènes peut être libérée dans le système d'exploitation.
Collection des ordures à Python
La collecte des ordures est définie comme le processus de remise en état ou de libération de la mémoire allouée lorsqu'elle n'est plus nécessaire par le programme.
La collecte des ordures (GC) est une forme de gestion automatique de la mémoire.Le collecteur des ordures tente de récupérer la mémoire qui a été allouée par le programme, mais n'est plus référencée - également appelée ordures.-Wikipédia
Dans des langages comme C, le programmeur doit manquer manuellement la mémoire pour les objets inutilisés (collection de déchets d'objets inutilisés), tandis que la collecte des ordures à Python est automatiquement prise en charge par la langue elle-même.
Python utilise deux méthodes pour la collecte automatique des ordures:
- Collection des ordures sur la base du comptage des références.
- Collection générationnelle des ordures.
Expliquons d'abord ce qu'est un nombre de références, puis nous en apprendrez plus sur la collecte des ordures sur la base du comptage des références!
Décompte de référence
Comme nous l'avons vu plus tôt, Cpython crée en interne les propriétéstaper
etcomte d'arbitre
pour chaque objet.
Voyons un exemple pour mieux comprendre lecomte d'arbitre
propriété:
un = "mémoire"
Lorsque le code ci-dessus est exécuté, Cpython crée un objetmémoire
de typechaîne
, comme indiqué ci-dessous:
Le champcomte d'arbitre
Indique le nombre de références à l'objet.Nous savons que dans Python, les variables ne sont que des références aux objets.Dans l'exemple ci-dessus, la variableun
est la seule référence à l'objet Stringmémoire
.D'où lecomte d'arbitre
Valeur de l'objet Stringmémoire
est1
.
Nous pouvons obtenir le nombre de références de tout objet de Python en utilisant lehitCount
méthode.
Obtenons le nombre de références de l'objet Stringmémoire
:
importer systèmeref_count = système.hitCount(un)imprimer(ref_count) # Sortie: 2
La sortie du code ci-dessus est2
.Cela indique que l'objet Stringmémoire
est référencé par deux variables.Cependant, nous avons vu plus tôt que lemémoire
L'objet n'est référencé que par la variableun
.
Pourquoi la valeur du nombre de références est-elle2
pour l'objet Stringmémoire
Lorsque vous utilisez lehitCount
méthode?
Pour comprendre cela, considérons la définition duhitCount
méthode:
def hitCount(était): ...
Remarque: ce qui précèdehitCount
La définition est uniquement à des fins d'explication.
Ici, quand nous passons la variableun
à la méthodehitCount
, lemémoire
L'objet est également mentionné par le paramètreétait
de lahitCount
méthode.Par conséquent, le nombre de références dumémoire
L'objet est2
.
Ainsi, chaque fois que nous utilisons lehitCount
Méthode Pour obtenir le nombre de références d'un objet, le nombre de références sera toujours 1 de plus que le nombre réel de référence de l'objet.
Créons une autre variable,b
, cela pointe vers le même objet de chaînemémoire
:
b = un
Variablesun
etb
pointent tous les deux vers l'objet Stringmémoire
.Par conséquent, le nombre de références de l'objet Stringmémoire
sera 2, et la sortie duhitCount
la méthode sera 3.
importer systèmeref_count = hitCount(un)imprimer(ref_count) # Sortie: 3
Diminuer le nombre de références
Pour diminuer le nombre de références, nous devons supprimer les références à la variable.Voici un exemple:
b = Aucun
La variableb
ne pointe plus vers l'objet Stringmémoire
.Par conséquent, le nombre de références de l'objet Stringmémoire
diminuera également.
importer systèmeref_count = hitCount(un)imprimer(ref_count) # Sortie: 2
Diminuer le nombre de références en utilisant ledu
Mot-clé
Nous pouvons également utiliser ledu
mot-clé pour diminuer le nombre de références d'un objet.
Si nous attribuonsAucun
à la variableb
(b = aucun
),b
ne pointe plus à l'objet Stringmémoire
. Ledu
Le mot-clé fonctionne de la même manière et est utilisé pour supprimer la référence de l'objet, réduisant ainsi son nombre de références.
Veuillez noter que ledu
Le mot-clé ne supprime pas l'objet.
Considérez l'exemple suivant:
du b
Ici, nous supprimons simplement la référence deb
.L'objet Stringmémoire
n'est pas supprimé.
Voyons maintenant le nombre de référence de l'objet Stringmémoire
:
importer systèmeref_count = hitCount(b)imprimer(ref_count) # Sortie: 2
Parfait!Le nombre de références deb
est maintenant 2.
Récapitulons ce que nous avons appris sur le nombre de références!
Le nombre de références d'un objet augmente si nous attribuons le même objet à une nouvelle variable.Lorsque nous déréférencez l'objet en utilisant ledu
mot-clé ou en le faisant remarquerAucun
, le nombre de références de cet objet diminue.
Maintenant que nous avons une meilleure compréhension du concept du nombre de références à Python, découvrons comment fonctionne la collecte des ordures sur la base du nombre de références.
Collection des ordures sur la base du nombre de références
La collecte des ordures sur la base du nombre de références utilise le nombre de références de l'objet pour libérer ou récupérer la mémoire.Lorsque le nombre de références de l'objet est zéro, la collecte des ordures par Python entre en jeu et supprime l'objet de la mémoire.
Lorsqu'un objet est supprimé de mémoire, il peut déclencher la suppression d'autres objets.
Considérez l'exemple suivant:
importer systèmeX = "ordures"b = [X, 20]]ref_count_x = hitCount(X)imprimer(ref_count_x) # Sortie: 3ref_count_b = hitCount(b)imprimer(ref_count_b) # Sortie: 1
Ignorer l'augmentation du nombre de références en raison duhitCount
Méthode, le nombre de référence de l'objet Stringordures
est 2 (référencé par variableX
et référencé de la listeb
), et le nombre de références de l'objet de tableau est1
.
Ensuite, supprimons la variableX
:
du X
Le nombre de référence de l'objet Stringordures
est 1 en raison de l'objet de tableau[x, 20]
, comme indiqué ci-dessous:
Nous pouvons toujours accéder à l'objet Stringordures
À l'aide de la variableb
.
b[0]]# Sortie: ordures
Supprimons la variableb
:
du b
Une fois le code ci-dessus exécuté, le nombre de références de l'objet de tableau devient 0 et le collecteur de ordures supprimera l'objet Array.
Suppression de l'objet Array[x, 20]
Supprimera également la référenceX
de l'objet Stringordures
de l'objet Array.Cela fera compter la référence duordures
objet 0, et par conséquent, leordures
L'objet sera également collecté.Ainsi, la collecte des ordures d'un objet peut également déclencher une collection d'ordures d'objets auxquels l'objet se réfère.
La collecte des ordures basée sur le nombre de références est en temps réel!
La collecte des ordures est déclenchée dès que le nombre de références de l'objet devient 0. Il s'agit de l'algorithme de collecte des ordures principal de Python, et donc, il ne peut pas être désactivé.
La collecte des ordures sur la base du nombre de références ne fonctionne pas s'il y a des références cycliques.Pour libérer ou récupérer la mémoire des objets qui ont des références cycliques, Python utilise un algorithme de collecte de déchets générationnel.
Ensuite, nous allons discuter des références cycliques, puis aller plus loin pour mieux en savoir plus sur l'algorithme de collecte des ordures générationnels!
Références cycliques à Python
Une référence cyclique ou une référence circulaire est une condition dans laquelle un objet se fait référence ou lorsque deux objets différents se réfèrent l'un à l'autre.
Les références cycliques ne sont possibles qu'avec des objets de conteneur, tels queliste,dictionet objets définis par l'utilisateur.Il n'est pas possible avec les types de données immuables, tels queentiers,chars, oucordes.
Considérez l'exemple suivant:
importer systèmeb = liste()ref_count = système.hitCount(b) # Sortie: 2b.ajouter(b)ref_count = système.hitCount(b) # Sortie: 3
Comme le montre la représentation ci-dessus, l'objet de tableaub
fait référence à lui-même.
Supprimons la référenceb
:
du b
Une fois que nous supprimons la référenceb
, l'objet Array ne sera pas accessible à partir du code Python, mais il continuera d'exister en mémoire, comme le montre l'image ci-dessus.
Comme l'objet Array fait référence à lui-même, le nombre de références de l'objet de tableau ne deviendra jamais 0, et il ne sera jamais collecté par le collecteur de déchets de référence.
Voyons un autre exemple:
classe Classe.: passerclasse ClasseB: passerUN = Classe.()B = ClasseB()# Référence b à unUN.ref_b = B# Se référer à B de bB.Ref_a = UN
Ici, nous avons défini deux objets,objet_a
etobjet_b
, des classesClasse.
etClasseB
, respectivement.objet_a
est mentionné par variableUN
etobjet_b
est mentionné par variableB
.
objet_a
a une propriétéref_b
qui pointe versobjet_b
.De même, objetobjet_b
a une propriétéRef_a
qui pointe versobjet_a
.
objet_a
etobjet_b
peut être accessible dans le code Python à l'aide de variablesUN
etB
, respectivement.
Supprimons les variablesUN
etB
du UNdu B
Une fois que nous supprimons les variablesUN
etB
,objet_a
etobjet_b
Ne sera pas accessible à partir de la base de code Python, mais ils continueront d'exister en mémoire.
Le nombre de références de ces objets ne deviendra jamais nul, car ils se pointent les uns aux autres (par des propriétésRef_a
etref_b
).Par conséquent, ces objets ne seront jamais collectés par le collecteur de déchets de comptage de référence.
Comme le nombre de références des objets avec des références cycliques ne devient jamais 0, la méthode de collecte de déchets de référence ne nettoiera jamais ces objets.Pour de tels cas, Python fournit un autre algorithme de collecte des ordures appeléCollection de déchets générationnels.
Collection générationnelle des ordures à Python
L'algorithme de collecte de déchets générationnels résout le problème des objets de collecte de déchets qui ont des références circulaires.Comme les références circulaires ne sont possibles qu'avec des objets de conteneur, il scanne tous les objets de conteneur, détecte des objets avec des références circulaires et les supprime si elles peuvent être collectées à la poubelle.
Pour réduire le nombre d'objets numérisés, la collection de déchets générationnels ignore également les tuples ne contenant que des types immuables (par exemple, int et cordes).
Étant donné que la numérisation des objets et les cycles de détection est un processus long, l'algorithme de collecte des ordures générationnels n'est pas en temps réel.Il est déclenché périodiquement. Lorsque l'algorithme de collecte des ordures générationnels est déclenché, tout le reste est arrêté.Par conséquent, pour réduire le nombre de fois où la collecte de déchets générationnels est déclenchée, CPython classe les objets de conteneur en plusieurs générations et définit le seuil pour chacune de ces générations.La collecte des ordures générationnelles est déclenchée si le nombre d'objets dans une génération donnée dépasse le seuil défini.
CPython classe les objets en trois générations (générations 0, 1 et 2).Lorsqu'un nouvel objet est créé, il appartient à la première génération.Si cet objet n'est pas collecté lorsque Cpython exécute l'algorithme de collecte de déchets générationnels, il passe à la deuxième génération.Si l'objet n'est pas collecté lorsque Cpython exécute à nouveau la collection de déchets générationnels, il est déplacé vers la troisième génération.Il s'agit de la génération finale, et l'objet y restera.
Généralement, la plupart des objets sont collectés dans la première génération.
Lorsque la collection de déchets générationnels est déclenchée pour une génération donnée, elle collecte également toutes les jeunes générations.Par exemple, si la collecte des ordures est déclenchée pour la génération 1, elle collectera également les objets présents dans la génération 0.
La situation où les trois générations (0, 1 et 2) sont collectées des ordures est appeléecollection complète.Commecomplet
La collection implique la numérisation et la détection des cycles dans un grand nombre d'objets, Cpython essaie d'évitercomplet
collection autant que possible.
Nous pouvons modifier le comportement du collecteur de déchets générationnel en utilisant leGC
Module fourni par Python.La collection générationnelle des ordures peut être désactivée en utilisant leGC
module.Par conséquent, il est également appelé collection de déchets en option.
Ensuite, nous expliquerons certaines des méthodes importantes duGC
module.
GC
Module en python
Nous pouvons utiliser la méthode suivante pour obtenir un seuil défini pour chaque génération:
importer GCGC.get_threshold()# Sortie: (700, 10, 10)
La sortie ci-dessus indique que le seuil de la première génération est700, alors que c'estdixpour les deuxième et troisième générations.
S'il y a plus de 700 objets dans la première génération, la collection de déchets générationnels sera déclenchée pour tous les objets de la première génération.S'il y a plus de 10 objets dans la 2e génération, la collection de déchets générationnels sera déclenchée pour les deux générations 1 et 0.
Nous pouvons également vérifier le nombre d'objets dans chaque génération, comme indiqué ci-dessous:
importer GCGC.get_count()# Sortie: (679, 8, 0) # Remarque: La sortie sera différente sur différentes machines
Ici, le nombre d'objets dans la première génération est de 679, tandis que le nombre d'objets dans la deuxième génération est de 8, et le nombre d'objets dans la troisième génération est de 0.
Nous pouvons également exécuter manuellement l'algorithme de collecte des ordures générationnels, comme indiqué ci-dessous:
importer GCGC.collecter()
GC.Collect ()
déclenchera la collection de déchets générationnels.Par défaut, il exécute une collection complète.Pour exécuter la collection de déchets générationnels pour les 1er générations, nous pouvons appeler cette méthode comme indiqué ci-dessous:
importer GCGC.collecter(génération=1)
Vérifions le nombre d'objets qui sont encore en vie après le processus de collecte,
importer GCGC.get_count()# Sortie: (4, 0, 0) # Remarque: La sortie sera différente sur différentes machines
Nous pouvons également mettre à jour le seuil de génération pour chaque génération, comme indiqué ci-dessous:
importer GCGC.get_threshold()GC.set_threshold(800, 12, 12)GC.get_threshold()# Sortie: (800, 12, 12) # Remarque: La sortie sera différente sur différentes machines
Nous pouvons désactiver la collection de déchets générationnels en utilisant la commande suivante:
GC.désactiver()
Inversement, nous pouvons l'activer à nouveau en utilisant la commande suivante:
GC.activer()
Conclusion
Dans cet article, nous avons appris que Python est un ensemble de règles et de spécifications, et Cpython est la mise en œuvre de référence de Python dans C. Nous avons également appris sur divers allocateurs de mémoire, tels que * l'allocateur d'objet *,Allocateur spécifique à l'objet,Allocateur de mémoire bruteetAllocateur à usage général, que Python utilise pour gérer efficacement la mémoire.Nous avons ensuite appris sur les arènes, les pools et les blocs que le gestionnaire de mémoire Python utilise pour optimiser l'allocation / désallocation de mémoire pour les petits objets (moins ou égaux à 512 octets de taille) dans un programme.Nous avons également appris les algorithmes de collecte des ordures, tels que le comptage de référence et la collection de déchets de génération.