Perdite di memoria in Python [chiuso]


180

Ho uno script di lunga durata che, se lasciato funzionare abbastanza a lungo, consumerà tutta la memoria del mio sistema.

Senza entrare nei dettagli della sceneggiatura, ho due domande:

  1. Ci sono delle "Best Practices" da seguire, che aiuteranno a prevenire le perdite?
  2. Quali tecniche ci sono per eseguire il debug delle perdite di memoria in Python?

5
Ho trovato utile questa ricetta .
David Schein,

Sembra che vengano stampati troppi dati per essere utili
Casebash,

1
@Casebash: se quella funzione stampa qualcosa, lo stai facendo seriamente. Elenca gli oggetti con __del__metodo a cui non viene più fatto riferimento ad eccezione del loro ciclo. Il ciclo non può essere interrotto a causa di problemi con __del__. Aggiustalo!
Helmut Grohne,

Risposte:



83

Ho provato la maggior parte delle opzioni menzionate in precedenza, ma ho trovato questo pacchetto piccolo e intuitivo il migliore: pympler

È abbastanza semplice rintracciare oggetti che non sono stati raccolti in modo inutile, controlla questo piccolo esempio:

Installa il pacchetto tramite pip install pympler

from pympler.tracker import SummaryTracker
tracker = SummaryTracker()

# ... some code you want to investigate ...

tracker.print_diff()

L'output mostra tutti gli oggetti che sono stati aggiunti, oltre alla memoria che hanno consumato.

Uscita campione:

                                 types |   # objects |   total size
====================================== | =========== | ============
                                  list |        1095 |    160.78 KB
                                   str |        1093 |     66.33 KB
                                   int |         120 |      2.81 KB
                                  dict |           3 |       840 B
      frame (codename: create_summary) |           1 |       560 B
          frame (codename: print_diff) |           1 |       480 B

Questo pacchetto offre numerose altre funzionalità. Consultare la documentazione del pympler , in particolare la sezione Identificazione delle perdite di memoria .


5
Vale la pena notare che pymplerpuò essere LENTO . Se stai facendo qualcosa di semi-in tempo reale, può paralizzare completamente le prestazioni della tua applicazione.
Nome falso

@sebpiq stranamente, lo stesso succede a me ... hai idea del perché questo stia accadendo? Una rapida occhiata al codice sorgente non ha fornito spunti reali.
linusg

25

Lasciami consigliare lo strumento mem_top che ho creato

Mi ha aiutato a risolvere un problema simile

Mostra istantaneamente i principali sospettati di perdite di memoria in un programma Python


1
è vero ... ma dà molto poco in termini di utilizzo / spiegazione dei risultati
me_

@me_, questo strumento ha entrambe le sezioni "Utilizzo" e "Spiegazione del risultato" documentate. Dovrei aggiungere una spiegazione come "refs è il conteggio dei riferimenti dall'oggetto, tipi è il conteggio degli oggetti di questo tipo, byte è la dimensione dell'oggetto" - non sarebbe troppo ovvio documentarlo?
Denis Ryzhkov,

i documenti di utilizzo dello strumento forniscono una sola riga che dice "di tanto in tanto: logging.debug (mem_top ())", mentre la sua spiegazione dei risultati è l'esperienza di monitoraggio degli errori nella vita reale dell'autore senza contesto ... non è una specifica tecnica che dice un dev esattamente quello che stanno guardando ... Non sto bussando alla tua risposta ... mostra i sospetti di alto livello come fatturati ... non fornisce una documentazione adeguata per comprendere appieno il risultato dell'uso ... per esempio , nell'output "Spiegazione dei risultati" perché "GearmanJobRequest" è ovviamente un problema? nessuna spiegazione del perché ...
me_

1
immagino di aver inavvertitamente bussato al tuo strumento, tu sei l'autore ... non era prevista alcuna offesa ...
me_

6
@me_, ho appena aggiunto il passaggio successivo a "Utilizzo", aggiunta la sezione "Contatori", aggiunta spiegazione del motivo per cui Gearman era un sospetto in quell'esempio di vita reale, documentato ogni parametro facoltativo di "mem_top ()" nel codice, e caricato tutto come v0.1.7. Dai un'occhiata se qualcos'altro potrebbe essere migliorato. Grazie! )
Denis Ryzhkov,

18

Il modulo Tracemalloc è stato integrato come modulo integrato a partire da Python 3.4 e, a quanto pare, è disponibile anche per le versioni precedenti di Python come libreria di terze parti (non l'ho ancora testato).

Questo modulo è in grado di produrre i file e le linee precisi che hanno allocato più memoria. IMHO, queste informazioni sono infinitamente più preziose del numero di istanze allocate per ogni tipo (che finisce per essere un sacco di tuple il 99% delle volte, il che è un indizio, ma aiuta a malapena nella maggior parte dei casi).

Ti consiglio di usare tracemalloc in combinazione con pirasite . 9 volte su 10, l'esecuzione del frammento di top 10 in un guscio di pirasite fornisce informazioni e suggerimenti sufficienti per correggere la perdita entro 10 minuti. Tuttavia, se non riesci ancora a trovare la causa della perdita, la pirasite-shell in combinazione con gli altri strumenti citati in questo thread ti darà probabilmente anche altri suggerimenti. Dovresti anche dare un'occhiata a tutti gli helper extra forniti da pyrasite (come il visualizzatore di memoria).



12

Dovresti dare un'occhiata in particolare ai tuoi dati globali o statici (dati di lunga durata).

Quando questi dati crescono senza restrizioni, puoi anche avere problemi in Python.

Il garbage collector può solo raccogliere dati a cui non si fa più riferimento. Ma i tuoi dati statici possono collegare elementi di dati che dovrebbero essere liberati.

Un altro problema può essere rappresentato dai cicli di memoria, ma almeno in teoria il Garbage Collector dovrebbe trovare ed eliminare i cicli, almeno fintanto che non sono agganciati a dati di lunga durata.

Quali tipi di dati di lunga durata sono particolarmente problematici? Dai un'occhiata a tutti gli elenchi e dizionari: possono crescere senza limiti. Nei dizionari potresti anche non vedere il problema che si presenta da quando accedi a dicts, il numero di chiavi nel dizionario potrebbe non essere di grande visibilità per te ...


7

Per rilevare e individuare perdite di memoria per processi a esecuzione prolungata, ad esempio in ambienti di produzione, è ora possibile utilizzare stackimpact . Usa tracemalloc sotto. Maggiori informazioni in questo post .

inserisci qui la descrizione dell'immagine


4

Per quanto riguarda le migliori pratiche, tieni d'occhio le funzioni ricorsive. Nel mio caso mi sono imbattuto in problemi di ricorsione (dove non c'era bisogno di essere). Un esempio semplificato di ciò che stavo facendo:

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    if my_flag:  # restart the function if a certain flag is true
        my_function()

def main():
    my_function()

operare in questo modo ricorsivo non attiverà la garbage collection e cancellerà i resti della funzione, quindi ogni volta che l'utilizzo della memoria è in continua crescita.

La mia soluzione era estrarre la chiamata ricorsiva da my_function () e fare in modo che main () gestisse quando chiamarla di nuovo. in questo modo la funzione termina in modo naturale e pulisce dopo se stessa.

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    .....
    return my_flag

def main():
    result = my_function()
    if result:
        my_function()

7
L'uso della ricorsione in questo modo si interromperà anche se si raggiunge il limite di profondità della ricorsione poiché Python non ottimizza le chiamate di coda. Per impostazione predefinita, si tratta di 1000 chiamate ricorsive.
Lie Ryan,

3

Non sono sicuro di "Best Practices" per le perdite di memoria in Python, ma Python dovrebbe cancellare la propria memoria dal Garbage Collector. Quindi principalmente inizierei controllando l'elenco circolare di alcuni cortometraggi, dal momento che non saranno raccolti dal garbage collector.


3
o riferimenti a oggetti che vengono conservati per sempre, ecc.
Matt b,

3
Potete per favore fornire esempi di elenchi circolari e oggetti che vengono conservati per sempre?
Daniel,

2

Questo non è affatto un consiglio esauriente. Ma la cosa numero uno da tenere a mente quando si scrive con il pensiero di evitare future perdite di memoria (loop) è assicurarsi che tutto ciò che accetta un riferimento a una richiamata, dovrebbe memorizzare tale richiamata come riferimento debole.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.