2009-09-16 14 views
112

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

Senza entrare nei dettagli della sceneggiatura, ho due domande:

  1. Ci sono dei "Best Practices" da seguire sono, che vi aiuterà a prevenire le perdite che si verificano?
  2. Quali tecniche sono disponibili per eseguire il debug di perdite di memoria in Python?
+3

Ho trovato [questa ricetta] (http://code.activestate.com/recipes/65333/) utile. –

+0

Sembra stampare troppo perché i dati siano utili – Casebash

+1

@Casebash: Se la funzione stampa qualcosa, si sta seriamente sbagliando. Elenca gli oggetti con il metodo '__del__' a cui non si fa più riferimento tranne per il loro ciclo. Il ciclo non può essere interrotto a causa di problemi con '__del__'. Aggiustalo! –

risposta

4

Non sono sicuro delle "migliori pratiche" per perdite di memoria in python, ma python dovrebbe cancellare la propria memoria dal proprio garbage collector. Quindi, principalmente, vorrei iniziare controllando la lista circolare di alcuni brevi, poiché non verranno raccolti dal garbage collector.

+3

o riferimenti a oggetti che sono tenuti per sempre, ecc. –

+0

Potete ragazzi fornire esempi di liste e oggetti circolari che vengono tenuti per sempre? – Daniel

8

Si consiglia di dare un'occhiata ai dati globali o statici (dati a lunga durata).

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

Il garbage collector può solo raccogliere dati, che non sono più referenziati. 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 finché non sono agganciati a dati di lunga durata.

Quali tipi di dati di lunga vita sono particolarmente fastidiosi? Dai un'occhiata a qualsiasi elenco e dizionario: possono crescere senza limiti. Nei dizionari potresti anche non vedere il problema da quando accedi a dicts, il numero di chiavi nel dizionario potrebbe non essere di grande visibilità per te ...

3

Questo non è affatto un consiglio esaustivo. Ma la prima cosa da tenere a mente quando si scrive con l'idea di evitare future perdite di memoria (loops) è assicurarsi che qualsiasi cosa che accetti un riferimento a una richiamata, debba memorizzare quella richiamata come riferimento debole.

13

Lasciatemi consigliare lo strumento mem_top,
che mi ha aiutato a risolvere un problema simile.

Mostra immediatamente i principali sospetti per perdite di memoria in un programma Python.

+0

questo è vero ... ma fornisce molto poco in termini di utilizzo/spiegazione dei risultati –

+0

@me_, questo strumento ha entrambe le sezioni "Uso" e "Risultato spiegazione" documentate. Dovrei aggiungere una spiegazione come "refs è il conteggio dei riferimenti dall'oggetto, i tipi è il conteggio di oggetti di questo tipo, i byte sono le dimensioni dell'oggetto" - non sarebbe troppo ovvio documentarlo? –

+0

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

51

ho provato la maggior parte delle opzioni indicate in precedenza, ma pensano che questa piccola e intuitivo pacchetto di essere il migliore: pympler

E 'abbastanza dritto in avanti per tracciare gli oggetti che non erano garbage collection, controllare questo piccolo esempio:

pacchetto di installazione 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, più la memoria che consumavano.

uscita Esempio:

        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 fornisce un certo numero di altre caratteristiche. Controllare pympler's documentation, in particolare la sezione Identifying memory leaks.

+1

Cosa divertente ... la mia perdita di memoria in realtà è scomparsa quando ho iniziato a utilizzare Pimpler per provare a seguirlo. Probabilmente un problema di garbage collection ... – sebpiq

+1

Vale la pena notare che 'pympler' può essere ** SLOW **. Se stai facendo qualcosa in tempo reale, può paralizzare completamente le prestazioni dell'applicazione. –

2

Per quanto riguarda le migliori pratiche, tenere d'occhio le funzioni ricorsive. Nel mio caso mi sono imbattuto in problemi con ricorsione (dove non c'era bisogno di essere). Un esempio semplificato di quello 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() 

Operando in questo modo ricorsivo non attiverà la raccolta dei rifiuti e ripulire i resti della funzione, così ogni volta che attraverso l'utilizzo della memoria è in crescita e in crescita.

La mia soluzione era di estrarre la chiamata ricorsiva da my_function() e avere l'handle main() quando richiamarla di nuovo. in questo modo la funzione termina naturalmente e si ripulisce da sola.

+4

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 tail. Per impostazione predefinita, si tratta di 1000 chiamate ricorsive. –

3

Per rilevare e individuare perdite di memoria per processi di lunga durata, ad es. negli ambienti di produzione, ora è possibile utilizzare stackimpact. Utilizza tracemalloc sotto. Maggiori informazioni in this post.

enter image description here

4

Tracemalloc module è stata integrata da un modulo incorporato a partire da Python 3.4, e appearently, è disponibile per le versioni precedenti di Python anche come a third-party library (non hanno provato però).

Questo modulo è in grado di emettere i file e le linee precise che hanno assegnato più memoria. IMHO, questa informazione è infinitamente più preziosa del numero di istanze allocate per ogni tipo (che finisce per essere un sacco di tuple il 99% delle volte, che è un indizio, ma a malapena aiuta nella maggior parte dei casi).

Si consiglia di utilizzare tracemalloc in combinazione con pyrasite. 9 volte su 10, eseguendo il top 10 snippet in un pyrasite-shell, verranno fornite sufficienti informazioni e suggerimenti per correggere la perdita entro 10 minuti. Tuttavia, se non si è ancora in grado di trovare la causa della perdita, la conchiglia di pirassite in combinazione con gli altri strumenti menzionati in questa discussione probabilmente fornirà anche altri suggerimenti. Dovresti anche dare un'occhiata a tutti gli altri helper forniti da pyrasite (come il memory viewer).