Eryksun a répondu à la question n°1, et j'ai répondu à la question n°3 (la n°4 originale), mais répondons maintenant à la question n°2 :
Pourquoi libère-t-il 50.5mb en particulier - sur quoi est basée la quantité libérée ?
Ce qui est basé sur, finalement, toute une série de coïncidences à l'intérieur de Python et malloc
qui sont très difficiles à prévoir.
Tout d'abord, selon la façon dont vous mesurez la mémoire, il se peut que vous ne mesuriez que les pages effectivement mappées en mémoire. Dans ce cas, chaque fois qu'une page est échangée par le pager, la mémoire apparaîtra comme "libérée", même si elle n'a pas été libérée.
Ou bien vous mesurez les pages en cours d'utilisation, qui peuvent ou non compter les pages allouées mais jamais touchées (sur les systèmes qui surallouent de manière optimiste, comme Linux), les pages allouées mais marquées MADV_FREE
etc.
Si vous mesurez réellement les pages allouées (ce qui n'est en fait pas très utile, mais il semble que ce soit ce que vous demandez), et que les pages ont réellement été désallouées, il y a deux circonstances dans lesquelles cela peut se produire : Soit vous avez utilisé brk
ou équivalent pour rétrécir le segment de données (très rare de nos jours), ou vous avez utilisé munmap
ou similaire pour libérer un segment cartographié. (Il existe aussi théoriquement une variante mineure de cette dernière, en ce sens qu'il existe des moyens de libérer une partie d'un segment mappé - par exemple, le voler avec MAP_FIXED
pour un MADV_FREE
segment que vous démappez immédiatement).
Mais la plupart des programmes n'allouent pas directement des éléments à partir de pages de mémoire ; ils utilisent un système de gestion de l'information. malloc
-allocateur de style. Lorsque vous appelez free
l'allocateur ne peut libérer des pages pour le système d'exploitation que si vous êtes juste free
dans le dernier objet vivant d'un mappage (ou dans les N dernières pages du segment de données). Il n'y a aucun moyen pour votre application de prédire raisonnablement cette situation, ni même de détecter qu'elle s'est produite à l'avance.
CPython rend les choses encore plus compliquées : il dispose d'un allocateur d'objets personnalisé à deux niveaux au-dessus d'un allocateur de mémoire personnalisé au-dessus de malloc
. (Voir les commentaires de la source pour une explication plus détaillée). Et pour couronner le tout, même au niveau de l'API C, et encore moins en Python, vous ne contrôlez même pas directement le moment où les objets de haut niveau sont désalloués.
Ainsi, lorsque vous libérez un objet, comment savez-vous s'il va libérer de la mémoire pour le système d'exploitation ? Eh bien, vous devez d'abord savoir que vous avez libéré la dernière référence (y compris toutes les références internes que vous ne connaissiez pas), permettant ainsi au GC de le désallouer. (Contrairement à d'autres implémentations, au moins CPython désallouera un objet dès qu'il y sera autorisé). Cela désalloue généralement au moins deux choses au niveau inférieur (par exemple, pour une chaîne de caractères, vous libérez la propriété PyString
et le tampon de chaîne).
Si vous faire désallouer un objet, pour savoir si cela entraîne la désallocation d'un bloc de stockage d'objets par le niveau inférieur suivant, vous devez connaître l'état interne de l'allocateur d'objets, ainsi que la façon dont il est implémenté. (Cela ne peut évidemment pas se produire à moins que vous ne désallouiez le dernier objet du bloc, et même dans ce cas, cela peut ne pas se produire).
Si vous faire désallouer un bloc de stockage objet, pour savoir si cela provoque une free
vous devez connaître l'état interne de l'allocateur PyMem, ainsi que la façon dont il est implémenté. (Encore une fois, vous devez être en train de désallouer le dernier bloc utilisé à l'intérieur d'un fichier malloc
et même dans ce cas, cela peut ne pas se produire).
Si vous faire free
a malloc
ée, pour savoir si cela provoque un munmap
ou équivalent (ou brk
), vous devez connaître l'état interne de la fonction malloc
ainsi que la manière dont il est mis en œuvre. Et celle-ci, contrairement aux autres, est hautement spécifique à la plateforme. (Et encore une fois, vous devez généralement désallouer la dernière mémoire utilisée. malloc
dans un mmap
segment, et même dans ce cas, cela peut ne pas se produire).
Donc, si vous voulez comprendre pourquoi il a libéré exactement 50,5 mégaoctets, vous allez devoir le retracer de bas en haut. Pourquoi est-ce que malloc
démapper 50.5mb de pages quand vous avez fait ces un ou plusieurs free
(pour probablement un peu plus de 50.5mb) ? Vous devez lire les règles de votre plateforme. malloc
puis parcourir les différents tableaux et listes pour voir son état actuel. (Sur certaines plateformes, il peut même utiliser des informations au niveau du système, ce qui est pratiquement impossible à capturer sans faire un instantané du système pour l'inspecter hors ligne, mais heureusement, ce n'est généralement pas un problème). Et ensuite, vous devez faire la même chose aux 3 niveaux supérieurs.
Donc, la seule réponse utile à la question est "Parce que".
À moins que vous ne fassiez du développement à ressources limitées (par exemple, embarqué), vous n'avez aucune raison de vous soucier de ces détails.
Et si vous sont dans le cadre d'un développement à ressources limitées, il est inutile de connaître ces détails ; il faut pratiquement contourner tous ces niveaux et se concentrer sur les aspects suivants mmap
la mémoire dont vous avez besoin au niveau de l'application (éventuellement avec un allocateur de zone simple, bien compris et spécifique à l'application entre les deux).
8 votes
Il convient de noter que ce comportement n'est pas spécifique à Python. En général, lorsqu'un processus libère de la mémoire allouée au tas, la mémoire n'est pas restituée au système d'exploitation avant la mort du processus.
0 votes
Votre question demande plusieurs choses, dont certaines sont inutiles, d'autres sont inappropriées pour l'OS, et d'autres encore pourraient être de bonnes questions. Demandez-vous si Python ne libère pas de mémoire, dans quelles circonstances exactes il peut/ne peut pas le faire, quel est le mécanisme sous-jacent, pourquoi il a été conçu de cette façon, s'il existe des solutions de contournement, ou quelque chose d'entièrement différent ?
4 votes
@abarnert J'ai combiné des sous-questions qui étaient similaires. Pour répondre à vos questions : Je sais que Python libère une partie de la mémoire au système d'exploitation, mais pourquoi pas toute la mémoire et pourquoi la quantité qu'il libère. S'il y a des circonstances où il ne peut pas le faire, pourquoi ? Quelles sont les solutions de contournement ?
0 votes
Duplicata possible de Comment puis-je libérer explicitement de la mémoire en Python ?
2 votes
@jww Je ne pense pas. Cette question concernait en fait la raison pour laquelle le processus d'interprétation n'a jamais libéré de mémoire, même après avoir entièrement collecté les déchets avec des appels à
gc.collect
.