Gestion de la mémoire à Python (2023)

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 ledefmot-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:Mallocetgratuit!

Que sontMallocetgratuitFonctions en C?

D'abord,Mallocest 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 leMallocMéthode pour obtenir la mémoire requise.

Deuxième,gratuitest 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 leMallocMéthode pour l'allouer.Lorsque le programme n'a plus besoin de la mémoire, Cpython appelle legratuitMé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

Memory Management in Python (1)

Lorsque le code ci-dessus est exécuté, Cpython crée un objet de typeentieret alloue la mémoire à cet objet sur la mémoire du tas.

Letaperindique le type de l'objet dans cpython et levaleurLe champ, comme son nom l'indique, stocke la valeur de l'objet (100dans ce cas).Nous discuterons duref_countchamp 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 variableunpointe vers cet objet entier comme indiqué ci-dessous:

Memory Management in Python (2)

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 variablesunetbLes deux pointent vers le même objet entier, comme indiqué ci-dessous:

Memory Management in Python (3)

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 valeur101et rend variableunpointez ce nouvel objet entier.Variablebcontinuera à pointer vers l'objet entier avec la valeur100, comme indiqué ci-dessous:

Memory Management in Python (4)

Ici, nous pouvons voir qu'au lieu d'écraser la valeur de100avec101, Cpython crée un nouvel objet avec la valeur101Parce 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 queboucle qui augmente la valeur de la variablejeJusqu'à 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 leMallocMéthode pour chaque nouvel objet pour allouer la mémoire à cet objet.Il appelle legratuitMéthode pour supprimer l'ancien objet de la mémoire.

Converons le code ci-dessus en termes deMallocetgratuit:

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 leMallocetgratuitMé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 appelerMallocetgratuitpour 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:

Memory Management in Python (5)

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.

  1. Mémoire non object de noyau Python: Partie de la mémoire allouée aux données non-objet Python Core.
  2. Tampons internes: Partie de la mémoire allouée aux tampons internes.
  3. 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.
  4. Mémoire de l'objet: Partie de la mémoire allouée aux objets.

Lorsque le programme demande de la mémoire, CPython utilise leMallocMéthode pour demander cette mémoire dans le système d'exploitation et le tas privé augmente en taille.

Pour éviter d'appelerMallocetgratuitPour 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 leMallocetgratuitMéthodes fréquemment, Cpython définit une hiérarchie d'allocateurs, comme indiqué ci-dessous:

Memory Management in Python (6)

Voici la hiérarchie des allocateurs de mémoire du niveau de base:

  • Allocateur à usage général (Cpython'sMallocmé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éralallocateur.Leà usage généralL'allocateur est leMallocMé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éralL'allocateur est Pythonmémoire bruteallocateur.Lemémoire bruteL'allocateur fournit une abstraction auà usage généralallocateur (c'est-à-dire leMallocméthode).Lorsqu'un processus Python a besoin de mémoire, lemémoire bruteL'allocateur interagit avec leà usage généralallocateur 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 bruteallocateur, 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 bruteallocateur.

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

Memory Management in Python (7)

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'appelerMallocpour 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ènesefficacement, Cpython diviseArènedansPiscines.Pools sont 4 Ko.Alors, unarèneCAN se compose de 64 piscines (256 Ko / 4Kb).

Memory Management in Python (8)

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:

Memory Management in Python (9)

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:

  1. Utilisé: Une piscine est censée être dans leutiliséÉtat s'il a des blocs disponibles pour l'allocation.

  2. Complet: Une piscine est censée être dans lecompletÉtat si tous les blocs de la piscine sont alloués.

  3. VideUne piscine serait dans levideÉ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 termesidxIndique la classe de taille de la piscine.Sisidxest 0 pour une piscine, il n'aura que des blocs de taille de taille 0 (c'est-à-dire des blocs de 8 octets).

Le termearenaindexIndique 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.LeNextpoolpointeur pointe vers le pool suivant de la même classe de taille, tandis que leprevpoolLe 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:

Memory Management in Python (10)

Lebloc libreLe 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 libreaiguille.

Memory Management in Python (11)

Comme on le voit dans la figure ci-dessus,allouéLes blocs sont alloués aux objets, tandis quegratuitLes 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.LeBleuLa 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.

Memory Management in Python (12)

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 libreLe pointeur pointe vers ce bloc.

Cpython maintient un tableau appeléusagepoolspour garder une trace des piscines dans leutiliséÉtat (pools disponibles pour les allocations) de toutes les classes de taille.

L'indice duusagepoolsLe tableau est égal à la classe de taille de la piscine.Pour chaque indexjede lausagepoolstableau,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:

Memory Management in Python (13)

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 leusagepoolstableau.

SiusagePools [i]pointe versnul, cela signifie qu'il n'y a pas de piscines de classe de taillejedans leutiliséÉtat.Si un objet demande un bloc de classe de tailleje, Cpython taillera un nouveau pool de cours de taillejeet 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.

Memory Management in Python (14)

Comme on le voit dans l'image ci-dessus,poolest 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 depooldevientutilisé, 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é;};

LefreepoolsLe pointeur pointe vers la liste des pools gratuits, et les piscines gratuites n'ont aucun de leurs blocs alloués.

LenfrepoolsLe terme indique le nombre de piscines libres dans les arènes.

CPython maintient une liste doublement liée appeléeusable_arenaspour garder une trace de toutes les arènes avecdisponiblepiscines.

Disponibleles piscines sont dans levideouutiliséÉtat.

LeNextarenaLe pointeur pointe vers l'arène utile suivante, tandis que letrichéLe pointeur pointe vers l'arène utilisable précédente dans leusable_arenasListe doublement liée.La liste doublement liée est triée dans l'ordre croissant dunfrepoolsvaleur.

Memory Management in Python (15)

Comme montré ci-dessus,usable_arenasest 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_arenassont 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:

  1. Collection des ordures sur la base du comptage des références.
  2. 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éstaperetcomte d'arbitrepour chaque objet.

Voyons un exemple pour mieux comprendre lecomte d'arbitrepropriété:

un = "mémoire"

Lorsque le code ci-dessus est exécuté, Cpython crée un objetmémoirede typechaîne, comme indiqué ci-dessous:

Memory Management in Python (16)

Le champcomte d'arbitreIndique 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 variableunest la seule référence à l'objet Stringmémoire.D'où lecomte d'arbitreValeur de l'objet Stringmémoireest1.

Nous pouvons obtenir le nombre de références de tout objet de Python en utilisant lehitCountmé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émoireest référencé par deux variables.Cependant, nous avons vu plus tôt que lemémoireL'objet n'est référencé que par la variableun.

Pourquoi la valeur du nombre de références est-elle2pour l'objet StringmémoireLorsque vous utilisez lehitCountméthode?

Pour comprendre cela, considérons la définition duhitCountméthode:

def hitCount(était): ...

Remarque: ce qui précèdehitCountLa définition est uniquement à des fins d'explication.

Ici, quand nous passons la variableunà la méthodehitCount, lemémoireL'objet est également mentionné par le paramètreétaitde lahitCountméthode.Par conséquent, le nombre de références dumémoireL'objet est2.

Memory Management in Python (17)

Ainsi, chaque fois que nous utilisons lehitCountMé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

Memory Management in Python (18)

Variablesunetbpointent tous les deux vers l'objet Stringmémoire.Par conséquent, le nombre de références de l'objet Stringmémoiresera 2, et la sortie duhitCountla 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 variablebne pointe plus vers l'objet Stringmémoire.Par conséquent, le nombre de références de l'objet Stringmémoirediminuera également.

importer systèmeref_count = hitCount(un)imprimer(ref_count) # Sortie: 2

Diminuer le nombre de références en utilisant leduMot-clé

Nous pouvons également utiliser ledumot-clé pour diminuer le nombre de références d'un objet.

Si nous attribuonsAucunà la variableb(b = aucun),bne pointe plus à l'objet Stringmémoire. LeduLe 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 leduLe mot-clé ne supprime pas l'objet.

Considérez l'exemple suivant:

du b

Memory Management in Python (19)

Ici, nous supprimons simplement la référence deb.L'objet Stringmémoiren'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 debest 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 ledumot-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

Memory Management in Python (20)

Ignorer l'augmentation du nombre de références en raison duhitCountMéthode, le nombre de référence de l'objet Stringorduresest 2 (référencé par variableXet 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 Stringorduresest 1 en raison de l'objet de tableau[x, 20], comme indiqué ci-dessous:

Memory Management in Python (21)

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érenceXde l'objet Stringorduresde l'objet Array.Cela fera compter la référence duorduresobjet 0, et par conséquent, leorduresL'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

Memory Management in Python (22)

Comme le montre la représentation ci-dessus, l'objet de tableaubfait référence à lui-même.

Supprimons la référenceb:

du b

Memory Management in Python (23)

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

Memory Management in Python (24)

Ici, nous avons défini deux objets,objet_aetobjet_b, des classesClasse.etClasseB, respectivement.objet_aest mentionné par variableUNetobjet_best mentionné par variableB.

objet_aa une propriétéref_bqui pointe versobjet_b.De même, objetobjet_ba une propriétéRef_aqui pointe versobjet_a.

objet_aetobjet_bpeut être accessible dans le code Python à l'aide de variablesUNetB, respectivement.

Supprimons les variablesUNetB

du UNdu B

Memory Management in Python (25)

Une fois que nous supprimons les variablesUNetB,objet_aetobjet_bNe 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_aetref_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.CommecompletLa collection implique la numérisation et la détection des cycles dans un grand nombre d'objets, Cpython essaie d'évitercompletcollection autant que possible.

Nous pouvons modifier le comportement du collecteur de déchets générationnel en utilisant leGCModule fourni par Python.La collection générationnelle des ordures peut être désactivée en utilisant leGCmodule.Par conséquent, il est également appelé collection de déchets en option.

Ensuite, nous expliquerons certaines des méthodes importantes duGCmodule.

GCModule 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.

Les références

References

Top Articles
Latest Posts
Article information

Author: Dean Jakubowski Ret

Last Updated: 09/12/2023

Views: 6500

Rating: 5 / 5 (50 voted)

Reviews: 81% of readers found this page helpful

Author information

Name: Dean Jakubowski Ret

Birthday: 1996-05-10

Address: Apt. 425 4346 Santiago Islands, Shariside, AK 38830-1874

Phone: +96313309894162

Job: Legacy Sales Designer

Hobby: Baseball, Wood carving, Candle making, Jigsaw puzzles, Lacemaking, Parkour, Drawing

Introduction: My name is Dean Jakubowski Ret, I am a enthusiastic, friendly, homely, handsome, zealous, brainy, elegant person who loves writing and wants to share my knowledge and understanding with you.