Comment puis-je libérer explicitement de la mémoire en Python?

J'ai écrit un programme Python qui agit sur un gros fichier d'entrée pour créer quelques millions d'objets représentant des triangles. L'algorithme est:

  1. lire un fichier d'entrée
  2. traiter le fichier et créer une liste de triangles, représentés par leurs sommets
  3. affiche les sommets au format OFF: une liste de Sommets suivie d'une liste de triangles. Les triangles sont représentés par des indices dans la liste des sommets

L'exigence de désactiver que j'imprime le la liste complète des sommets avant d'imprimer les triangles signifie que je dois conserver la liste des triangles en mémoire avant d'écrire la sortie dans le fichier. En attendant, je reçois des erreurs de mémoire en raison de la taille des listes.

Quelle est la meilleure façon de dire à Python que je n'ai plus besoin de certaines données, et il peut être libéré?

236
demandé sur Aaron Hall 2009-08-22 23:10:35

9 réponses

Selon la Documentation officielle Python , Vous pouvez forcer le Garbage Collector à libérer de la mémoire non référencée avec gc.collect(). Exemple:

import gc
gc.collect()
244
répondu Havenard 2018-02-16 16:00:45

Malheureusement (en fonction de votre version et de votre version de Python) certains types d'objets utilisent des "listes libres" qui sont une optimisation locale soignée mais peuvent provoquer une fragmentation de la mémoire, en particulier en rendant de plus en plus de mémoire "réservée" uniquement aux objets d'un certain type et donc indisponible pour le "fonds général".

Le seul moyen vraiment fiable de s'assurer qu'une utilisation importante mais temporaire de la mémoire renvoie toutes les ressources au système quand c'est fait, est de faire en sorte que cette utilisation se produise dans un subprocess, qui fait le travail affamé de mémoire se termine alors. Dans de telles conditions, le système d'exploitation fera son travail et recyclera volontiers toutes les ressources que le sous-processus a pu engloutir. Heureusement, le module multiprocessing rend ce genre d'opération (qui était plutôt pénible) pas trop mal dans les versions modernes de Python.

Dans votre cas d'utilisation, il semble que le meilleur moyen pour les sous-processus d'accumuler des résultats tout en s'assurant que ces résultats sont disponibles pour le processus principal est d'utiliser des fichiers semi-temporaires (par semi-temporaire, je veux dire, pas le genre de fichiers qui disparaissent automatiquement lorsqu'ils sont fermés, juste des fichiers ordinaires que vous supprimez explicitement lorsque vous en avez terminé avec eux).

90
répondu Alex Martelli 2014-06-30 12:59:14

L'instruction del peut être utile, mais IIRC Il n'est pas garanti de libérer la mémoire . Les documents sont ici ... et un pourquoi il n'est pas publié est ici.

J'ai entendu des gens sur des systèmes de type Linux et Unix bifurquer un processus python pour faire du travail, obtenir des résultats puis le tuer.

Cet article a des notes sur le garbage collector Python, mais je pense que le manque de contrôle de la mémoire est l'inconvénient de la mémoire gérée

28
répondu Aiden Bell 2018-02-22 21:54:48

Python est collecté, donc si vous réduisez la taille de votre liste, il récupérera de la mémoire. Vous pouvez également utiliser l'instruction" del " pour vous débarrasser complètement d'une variable:

biglist = [blah,blah,blah]
#...
del biglist
20
répondu Ned Batchelder 2009-08-22 19:14:30

Vous ne pouvez pas libérer explicitement la mémoire. Ce que vous devez faire est de vous assurer de ne pas conserver de références aux objets. Ils seront ensuite ramassés, libérant ainsi la mémoire.

Dans votre cas, lorsque vous avez besoin de grandes listes, vous devez généralement réorganiser le code, en utilisant généralement des générateurs/itérateurs à la place. De cette façon, vous n'avez pas besoin d'avoir les grandes listes en mémoire à tous.

Http://www.prasannatech.net/2009/07/introduction-python-generators.html

16
répondu Lennart Regebro 2009-08-22 19:16:44

(la libération de la mémoire se fait périodiquement automatiquement. del peut donc être votre ami, car il marque les objets comme étant supprimables.)

Peut-être que vous ne rencontrerez aucun problème de mémoire, en premier lieu, en utilisant une structure plus compacte pour vos données. Ainsi, les listes de nombres sont beaucoup moins efficaces en mémoire que le format utilisé par le module standard array ou le module tiers numpy. Vous économiseriez de la mémoire en plaçant vos sommets dans un tableau NumPy 3xN et vos triangles dans un n-élément tableau.

14
répondu Eric Lebigot 2017-08-07 20:30:59

D'autres ont posté des moyens que vous pourriez être en mesure d ' "amadouer" l'interpréteur Python pour libérer la mémoire (ou autrement éviter d'avoir des problèmes de mémoire). Les Chances sont que vous devriez essayer leurs idées en premier. Cependant, je pense qu'il est important de vous donner une réponse directe à votre question.

Il n'y a vraiment aucun moyen de dire directement à Python de libérer de la mémoire. Le fait est que si vous voulez un niveau de contrôle aussi bas, vous devrez écrire une extension en C ou C++.

Cela dit, il y a quelques outils pour vous aider:

7
répondu Jason Baker 2009-08-22 19:47:09

J'ai eu un problème similaire en lisant un graphique à partir d'un fichier. Le traitement comprenait le calcul d'une matrice flottante 200 000x200 000 (une ligne à la fois) qui ne rentrait pas dans la mémoire. Essayer de libérer la mémoire entre les calculs en utilisant gc.collect() A CORRIGÉ l'aspect lié à la mémoire du problème mais cela a entraîné des problèmes de performance: Je ne sais pas pourquoi mais même si la quantité de mémoire utilisée est restée constante, chaque nouvel appel à gc.collect() a pris plus de temps que le précédent. Donc assez rapidement le la collecte des ordures a pris la plupart du temps de calcul.

Pour résoudre à la fois les problèmes de mémoire et de performance, je suis passé à l'utilisation d'un truc multithreading que j'ai lu une fois quelque part (je suis désolé, Je ne peux plus trouver le post connexe). Avant de lire chaque ligne du fichier dans une grande boucle for, de le traiter et d'exécuter gc.collect() de temps en temps pour libérer de l'espace mémoire. Maintenant, j'appelle une fonction qui lit et traite une partie du fichier dans un nouveau thread. Une fois le thread terminé, la mémoire est automatiquement libéré sans le problème de performance étrange.

Pratiquement cela fonctionne comme ceci:

from dask import delayed  # this module wraps the multithreading
def f(storage, index, chunk_size):  # the processing function
    # read the chunk of size chunk_size starting at index in the file
    # process it using data in storage if needed
    # append data needed for further computations  to storage 
    return storage

partial_result = delayed([])  # put into the delayed() the constructor for your data structure
# I personally use "delayed(nx.Graph())" since I am creating a networkx Graph
chunk_size = 100  # ideally you want this as big as possible while still enabling the computations to fit in memory
for index in range(0, len(file), chunk_size):
    # we indicates to dask that we will want to apply f to the parameters partial_result, index, chunk_size
    partial_result = delayed(f)(partial_result, index, chunk_size)

    # no computations are done yet !
    # dask will spawn a thread to run f(partial_result, index, chunk_size) once we call partial_result.compute()
    # passing the previous "partial_result" variable in the parameters assures a chunk will only be processed after the previous one is done
    # it also allows you to use the results of the processing of the previous chunks in the file if needed

# this launches all the computations
result = partial_result.compute()

# one thread is spawned for each "delayed" one at a time to compute its result
# dask then closes the tread, which solves the memory freeing issue
# the strange performance issue with gc.collect() is also avoided
6
répondu Retzod 2018-08-21 06:15:17

Si vous ne vous souciez pas de la réutilisation des sommets, vous pourriez avoir deux fichiers de sortie-un pour les sommets et un pour les triangles. Ajoutez ensuite le fichier triangle au fichier vertex lorsque vous avez terminé.

3
répondu Nosredna 2009-08-22 19:29:19