2010-06-23 18 views
49

TL/DR:scaricare un modulo in Python

import gc, sys 

print len(gc.get_objects()) # 4073 objects in memory 

# Attempt to unload the module 

import httplib 
del sys.modules["httplib"] 
httplib = None 

gc.collect() 
print len(gc.get_objects()) # 6745 objects in memory 

UPDATE Ho contattato gli sviluppatori Python su questo problema e in effetti si tratta di not going to be possible to unload a module completamente "in prossimi cinque anni". (vedi il link)

Si prega di accettare che Python infatti non supporta i moduli di scarico per gravi, fondamentali, insormontabili, problemi tecnici, in 2.x.


Durante il mio recente caccia di un memleak nella mia app, ho ristretto la scelta a moduli, vale a dire la mia incapacità di spazzatura raccogliere un modulo scaricato. Utilizzando , qualsiasi metodo elencato di seguito per scaricare un modulo lascia migliaia di oggetti in memoria. In altre parole: non riesco a scaricare un modulo in Python ...

Il resto della domanda è cercare di raccogliere un modulo in qualche modo. prova

Let:

import gc 
import sys 

sm = sys.modules.copy() # httplib, which we'll try to unload isn't yet 
         # in sys.modules, so, this isn't the source of problem 

print len(gc.get_objects()) # 4074 objects in memory 

Salviamo una copia di sys.modules per tentare di ripristinare in un secondo momento. Quindi, questo è un oggetto di riferimento 4074. Dovremmo idealmente tornare a questo in qualche modo.

Diamo importare un modulo:

import httplib 
print len(gc.get_objects()) # 7063 objects in memory 

Siamo fino a 7K oggetti non spazzatura. Proviamo a rimuovere httplib da sys.modules.

sys.modules.pop('httplib') 
gc.collect() 
print len(gc.get_objects()) # 7063 objects in memory 

Bene, questo non ha funzionato. Hmm, ma non c'è un riferimento in __main__? Oh, sì:

del httplib 
gc.collect() 
print len(gc.get_objects()) # 6746 objects in memory 

Urrà, giù 300 oggetti. Tuttavia, nessun sigaro, è più di 4000 oggetti originali. Proviamo a ripristinare sys.modules dalla copia.

sys.modules = sm 
gc.collect() 
print len(gc.get_objects()) # 6746 objects in memory 

Hmmm, bene che era inutile, nessun cambiamento .. Forse se cancelliamo globali ...

globals().clear() 
import gC# we need this since gc was in globals() too 
gc.collect() 
print len(gc.get_objects()) # 6746 objects in memory 

gente del posto?

locals().clear() 
import gC# we need this since gc was in globals() too 
gc.collect() 
print len(gc.get_objects()) # 6746 objects in memory 

Cosa .. cosa succederebbe se imported un modulo all'interno di exec?

local_dict = {} 
exec 'import httplib' in local_dict 
del local_dict 
gc.collect() 
print len(gc.get_objects()) # back to 7063 objects in memory 

Ora, questo non è giusto, ha importato in __main__, perché? Non avrebbe dovuto lasciare mai il local_dict ... Argh! Torniamo a importare completamente httplib. Forse se lo abbiamo sostituito con un oggetto fittizio?

from types import ModuleType 
import sys 
print len(gc.get_objects()) # 7064 objects in memory 

Bloody ..... !!

sys.modules['httplib'] = ModuleType('httplib') 
print len(gc.get_objects()) # 7066 objects in memory 

moduli Die, die !!

import httplib 
for attr in dir(httplib): 
    setattr(httplib, attr, None) 
gc.collect() 
print len(gc.get_objects()) # 6749 objects in memory 

va bene, dopo tutti i tentativi, il migliore è 2675 (quasi + 50%) dal punto di partenza ... Questo è solo da un modulo ... Che non ha nemmeno nulla di grande dentro ...

Ok, ora sul serio, dov'è il mio errore? Come posso scaricare un modulo e cancellare tutto il suo contenuto? Oppure i moduli di Python sono una perdita di memoria gigante?

sorgente completo nel più semplice per copiare forma: http://gist.github.com/450606

risposta

17

Python non supporta i moduli di scarico.

Tuttavia, a meno che il programma carichi un numero illimitato di moduli nel tempo, questa non è la fonte della perdita di memoria. I moduli vengono normalmente caricati una volta all'avvio e il gioco è fatto. La tua perdita di memoria è probabilmente altrove.

Nel caso improbabile che il tuo programma carichi davvero un numero illimitato di moduli nel tempo, probabilmente dovresti riprogettare il tuo programma. ;-)

+1

Sì, carica un numero ragionevolmente illimitato di moduli: è un server di applicazioni Web che accetta nuove revisioni del proprio codice sorgente e lo ricarica (è un'attività web piuttosto standard). La perdita è dovuta al fatto che il vecchio codice è ancora disponibile in memoria, anche se sostituito, anche se non raggiungibile ... –

+0

Python supporta i moduli di scarico. Sono spazzatura raccolti, come ogni altro oggetto in Python. –

+1

@Slava: potresti dare un'occhiata al codice sorgente a 'mod_python', che ha il suo importatore che è progettato per gestire i moduli di ricarica senza produrre perdite di memoria. Potrebbe esserci del codice che potresti usare. –

0

(Si dovrebbe provare a scrivere le domande più concisi, ho letto solo l'inizio e scremato il resto.) Vedo un semplice problema alla partenza:

sm = sys.modules.copy() 

Lei ha fatto una copia di sys.modules, quindi ora la tua copia ha un riferimento al modulo, quindi ovviamente non verrà raccolto. Puoi vedere a cosa si riferisce con gc.get_referrers.

Questo funziona bene:

# module1.py 
class test(object): 
    def __del__(self): 
     print "unloaded module1" 
a = test() 

print "loaded module1" 

.

# testing.py 
def run(): 
    print "importing module1" 
    import module1 
    print "finished importing module1" 

def main(): 
    run() 
    import sys 
    del sys.modules["module1"] 
    print "finished" 

if __name__ == '__main__': 
    main() 

Module1 viene scaricato non appena togliamo da sys.modules, perché non ci sono rimasti i riferimenti al modulo. (Fare lo dopo l'importazione funzionerebbe anche - ho solo messo l'importazione in un'altra funzione per chiarezza.Tutto quello che devi fare è lasciar cadere i tuoi riferimenti ad esso.)

Ora, è un po 'complicato farlo in pratica , a causa di due problemi:

  • Per raccogliere un modulo, tutti i riferimenti al modulo devono essere irraggiungibili (come con la raccolta di qualsiasi oggetto). Ciò significa che tutti gli altri moduli che lo hanno importato devono essere anche dereferenziati e ricaricati.
  • Se si rimuove un modulo da sys.modules quando viene ancora fatto riferimento altrove, si è creata una situazione insolita: il modulo è ancora caricato e utilizzato dal codice, ma il caricatore di moduli non ne conosce più. La prossima volta che si importa il modulo, non si otterrà un riferimento a quello esistente (poiché si è cancellato il record di quello), quindi verrà caricata una seconda copia coesistente del modulo. Ciò può causare seri problemi di coerenza. Quindi, assicurati che non ci siano riferimenti rimanenti al modulo prima di rimuoverlo definitivamente da sys.modules.

Ci sono alcuni problemi difficili da usare in generale: rilevare quali moduli dipendono dal modulo che si sta scaricando; sapere se va bene scaricare anche quelli (dipende pesantemente dal tuo caso d'uso); maneggiando il threading mentre si esamina tutto questo (date un'occhiata a imp.acquire_lock), e così via.

Potrei inventare un caso in cui ciò potrebbe essere utile, ma la maggior parte delle volte raccomanderei di riavviare l'app se il suo codice cambia. Probabilmente ti dai solo mal di testa.

+8

Beh, per non essere snyde, ma dovresti aver letto la domanda, o almeno la parola "completamente" nel titolo (o almeno i tag). Il problema non è che non voglio ricaricare, il problema è * perdita di memoria * associata a qualsiasi tipo di rimozione (elencata) del modulo (compresi quelli che hai proposto, quali * sono * elencati nella mia domanda, insieme a una dozzina di altri). In realtà ho aggiunto 'sys.modules.copy()' in una fase molto avanzata, rimuoverlo non cambia nulla (prova te stesso). –

+1

La fonte, per provare: http://gist.github.com/450606. Prova a rimuovere sys.modules.copy e vedrai che c'è ancora più del 50% di aumento degli oggetti anche se tutti i riferimenti al modulo sono stati rimossi. –

+0

Vedi qui per brevità cosa non va (usando il tuo codice): http://gist.github.com/450726. Non provo a caricare -superare 'sys', dal momento che stiamo operando su' sys.modules', quindi uso 'httplib' - puoi provare qualsiasi altro. –

3

io non sono sicuro di Python, ma in altre lingue, chiamando l'equivalente di gc.collect() fa non rilascio memoria inutilizzata - sarà solo liberare quella memoria se/quando è effettivamente necessario la memoria.

Altrimenti, ha senso che Python conservi i moduli in memoria per il momento, nel caso in cui debbano essere caricati di nuovo.

+0

Il problema è che ho bisogno di sostituirli con nuove versioni. E anche quando lo sostituisco 1-a-1 con lo stesso modulo di dimensioni - l'utilizzo della memoria cresce (perdite) ... Grazie per il suggerimento, però. –

Problemi correlati