J'ai quelques questions liées à l'utilisation de la mémoire dans l'exemple suivant.
Si je cours dans l'interprète,
foo = ['bar' for _ in xrange(10000000)]
la vraie mémoire utilisée sur ma machine va jusqu'à
80.9mb
. Moi alors,del foo
la vraie mémoire descend, mais seulement pour
30.4mb
. L'interpréteur utilise la4.4mb
ligne de base, alors quel est l'avantage de ne pas libérer26mb
de mémoire vers le système d'exploitation? Est-ce parce que Python "planifie à l'avance", pensant que vous pourriez utiliser à nouveau autant de mémoire?Pourquoi libère-t-il
50.5mb
en particulier - sur quoi se base le montant libéré?Existe-t-il un moyen de forcer Python à libérer toute la mémoire utilisée (si vous savez que vous n'utiliserez plus autant de mémoire)?
REMARQUE
Cette question est différente de Comment puis-je libérer explicitement de la mémoire en Python?
car cette question traite principalement de l'augmentation de l'utilisation de la mémoire par rapport à la ligne de base, même après que l'interpréteur a libéré des objets via le ramasse-miettes (avec utilisation gc.collect
ou non).
la source
gc.collect
.Réponses:
La mémoire allouée sur le tas peut être sujette à des marques de marée haute. Ceci est compliqué par les optimisations internes de Python pour l'allocation de petits objets (
PyObject_Malloc
) dans 4 pools de KiB, classés pour des tailles d'allocation à des multiples de 8 octets - jusqu'à 256 octets (512 octets en 3.3). Les pools eux-mêmes sont dans des arènes de 256 Kio, donc si un seul bloc dans un pool est utilisé, toute l'arène de 256 Kio ne sera pas libérée. Dans Python 3.3, l'allocateur de petits objets a été basculé vers l'utilisation de mappages de mémoire anonymes au lieu du tas, il devrait donc mieux fonctionner pour libérer de la mémoire.En outre, les types intégrés conservent des listes libres d'objets précédemment alloués qui peuvent ou non utiliser l'allocateur de petits objets. Le
int
type maintient une liste libre avec sa propre mémoire allouée, et l'effacer nécessite un appelPyInt_ClearFreeList()
. Cela peut être appelé indirectement en faisant un fullgc.collect
.Essayez-le comme ça et dites-moi ce que vous obtenez. Voici le lien pour psutil.Process.memory_info .
Production:
Éditer:
Je suis passé à la mesure par rapport à la taille de la machine virtuelle du processus pour éliminer les effets des autres processus du système.
Le runtime C (par exemple, glibc, msvcrt) réduit le tas lorsque l'espace libre contigu en haut atteint un seuil constant, dynamique ou configurable. Avec la glibc, vous pouvez régler cela avec
mallopt
(M_TRIM_THRESHOLD). Compte tenu de cela, il n'est pas surprenant que le tas diminue de plus - voire de beaucoup plus - que le bloc que vousfree
.Dans 3.x
range
ne crée pas de liste, donc le test ci-dessus ne créera pas 10 millions d'int
objets. Même si c'était le cas, leint
type dans 3.x est fondamentalement un 2.xlong
, qui n'implémente pas de liste freelist.la source
memory_info()
place deget_memory_info()
etx
est définiint
s même en Python 3, mais chacun remplace le dernier de la variable de boucle afin qu'ils n'existent pas tous en même temps.Je suppose que la question qui vous tient vraiment à cœur ici est:
Non, il n'y en a pas. Mais il existe une solution simple: les processus enfants.
Si vous avez besoin de 500 Mo de stockage temporaire pendant 5 minutes, mais que vous devez ensuite exécuter pendant 2 heures supplémentaires et que vous ne toucherez plus jamais autant de mémoire, créez un processus enfant pour effectuer le travail gourmand en mémoire. Lorsque le processus enfant disparaît, la mémoire est libérée.
Ce n'est pas complètement trivial et gratuit, mais c'est assez facile et bon marché, ce qui est généralement assez bon pour que le commerce en vaille la peine.
Tout d'abord, le moyen le plus simple de créer un processus enfant est avec
concurrent.futures
(ou, pour 3.1 etfutures
versions antérieures, le backport sur PyPI):Si vous avez besoin d'un peu plus de contrôle, utilisez le
multiprocessing
module.Les coûts sont:
mmap
ped ou autre; les API de mémoire partagée dansmultiprocessing
; etc.).struct
-able ou idéalementctypes
-able).la source
eryksun a répondu à la question n ° 1, et j'ai répondu à la question n ° 3 (l'original n ° 4), mais répondons maintenant à la question n ° 2:
Ce sur quoi il est basé est, en fin de compte, toute une série de coïncidences à l'intérieur de Python et
malloc
qui sont très difficiles à prévoir.Premièrement, selon la façon dont vous mesurez la mémoire, vous ne mesurerez peut-être que des pages réellement mappées en mémoire. Dans ce cas, à chaque fois qu'une page est permuté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 vous pouvez mesurer les pages en cours d'utilisation, qui peuvent ou non compter les pages allouées mais jamais touchées (sur des systèmes qui surallouent de manière optimiste, comme Linux), les pages qui sont allouées mais balisées
MADV_FREE
, etc.Si vous mesurez vraiment les pages allouées (ce qui n'est en fait pas une chose très utile à faire, mais cela semble être ce que vous demandez), et que les pages ont vraiment été désallouées, deux circonstances dans lesquelles cela peut arriver: Soit vous '
brk
vous avez utilisé ou équivalent pour réduire le segment de données (très rare de nos jours), ou vous avez utilisémunmap
ou similaire pour libérer un segment mappé. (Il existe également en théorie une variante mineure de ce dernier, en ce sens qu'il existe des moyens de libérer une partie d'un segment mappé - par exemple, la voler avecMAP_FIXED
pour unMADV_FREE
segment que vous démappez immédiatement.)Mais la plupart des programmes n'allouent pas directement les choses à partir des pages de mémoire; ils utilisent un
malloc
allocateur de style. Lorsque vous appelezfree
, l'allocateur ne peut libérer des pages sur le système d'exploitation que si vous êtes simplementfree
le dernier objet actif 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évoir raisonnablement cela, ni même de détecter que cela s'est produit à l'avance.CPython rend cela encore plus compliqué: il a un allocateur d'objet personnalisé à 2 niveaux au-dessus d'un allocateur de mémoire personnalisé
malloc
. (Voir les commentaires source pour une explication plus détaillée.) Et en plus de cela, même au niveau de l'API C, encore moins Python, vous ne contrôlez même pas directement le moment où les objets de niveau supérieur sont désalloués.Alors, lorsque vous libérez un objet, comment savoir s'il va libérer de la mémoire dans le système d'exploitation? Eh bien, vous devez d'abord savoir que vous avez publié la dernière référence (y compris toutes les références internes que vous ne connaissiez pas), permettant au GC de la désallouer. (Contrairement à d'autres implémentations, au moins CPython désallouera un objet dès qu'il est autorisé à le faire.) Cela désalloue généralement au moins deux choses au niveau suivant (par exemple, pour une chaîne, vous libérez l'
PyString
objet et le tampon de chaîne ).Si vous faites désallouer un objet, à savoir si cela provoque le niveau suivant de désaffecter un bloc de stockage d'objets, vous devez connaître l'état interne de l'allocateur d'objet, ainsi que la façon dont il est mis en œuvre. (Cela ne peut évidemment pas arriver à moins que vous ne désallouiez la dernière chose du bloc, et même dans ce cas, cela peut ne pas arriver.)
Si vous faites désallouer un bloc de stockage d'objets, de savoir si cela provoque un
free
appel, vous devez connaître l'état interne de l'allocateur PyMem, ainsi que la façon dont il est mis en œuvre. (Encore une fois, vous devez désallouer le dernier bloc utilisé dans unemalloc
région ed, et même dans ce cas, cela peut ne pas arriver.)Si vous faites
free
unemalloc
région ed, pour savoir si cela provoque unmunmap
ou équivalent (oubrk
), vous devez connaître l'état interne dumalloc
, ainsi que comment il est implémenté. Et celui-ci, contrairement aux autres, est très spécifique à la plate-forme. (Et encore une fois, vous devez généralement désallouer le dernier en cours d'utilisationmalloc
dans unmmap
segment, et même dans ce cas, cela peut ne pas arriver.)Donc, si vous voulez comprendre pourquoi il est arrivé à libérer exactement 50,5 Mo, vous allez devoir le retracer de bas en haut. Pourquoi
malloc
avez-vous démappé 50,5 Mo de pages lorsque vous avez effectué ces un ou plusieursfree
appels (probablement un peu plus de 50,5 Mo)? Vous devrez lire votre plate-formemalloc
, puis parcourir les différents tableaux et listes pour voir son état actuel. (Sur certaines plates-formes, 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 à inspecter hors ligne, mais heureusement, ce n'est généralement pas un problème.) Et puis vous devez faites 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, intégrées), vous n'avez aucune raison de vous soucier de ces détails.
Et si vous êtes en train de faire le développement des ressources limitées, sachant que ces détails ne sert à rien; vous devez pratiquement faire une analyse finale autour de tous ces niveaux et plus particulièrement de
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).la source
Tout d'abord, vous voudrez peut-être installer des regards:
Ensuite, exécutez-le dans le terminal!
Dans votre code Python, ajoutez au début du fichier, ce qui suit:
Après avoir utilisé la variable "Big" (par exemple: myBigVar) pour laquelle vous souhaitez libérer de la mémoire, écrivez dans votre code python ce qui suit:
Dans un autre terminal, exécutez votre code python et observez dans le terminal "glances", comment la mémoire est gérée dans votre système!
Bonne chance!
PS Je suppose que vous travaillez sur un système Debian ou Ubuntu
la source