2010-06-23 7 views
49

TL/DR:Unload ein Modul 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 Ich habe Python-Entwickler über dieses Problem in Kontakt gebracht und in der Tat ist es not going to be possible to unload a module vollständig "in nächsten fünf Jahren". (siehe Link)

Bitte akzeptieren Sie, dass Python in der Tat keine Entlade-Module für schwere, grundlegende, unüberwindbare, technische Probleme in 2.x unterstützt.


Während meiner letzten Jagd nach einem memleak in meiner app, ich habe es verengt Module nach unten, nämlich meine Unfähigkeit, Müll sammeln ein unbelasteter Modul. Wenn Sie eine beliebige Methode verwenden, um ein Modul zu entladen, bleiben Tausende von Objekten im Speicher. Mit anderen Worten - ich kann ein Modul in Python nicht entladen ...

Der Rest der Frage ist der Versuch Müll irgendwie ein Modul zu sammeln.

Lassen Sie uns versuchen:

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 

Lassen Sie uns eine Kopie sys.modules speichern, zu versuchen, es später wieder herzustellen. Also, das ist eine Baseline 4074 Objekte. Wir sollten im Idealfall darauf zurückkommen.

Lasst uns ein Modul importieren:

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

Wir sind bis zu 7K Nicht-Müll-Objekte. Versuchen wir, httplib von sys.modules zu entfernen.

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

Nun, das hat nicht funktioniert. Hmm, aber gibt es keine Referenz in __main__? Oh, yeah:

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

Hurra, runter 300 Objekte. Immer noch keine Zigarre, das sind weit mehr als 4000 Originalobjekte. Versuchen wir, sys.modules von Kopie wiederherzustellen.

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

Hmmm, na ja, das war sinnlos, keine Änderung .. Vielleicht, wenn wir Globals auszulöschen ...

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

Einheimischen?

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

Was .. was ist, wenn wir imported ein Modul innerhalb von 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 

Nun, das ist nicht fair, es importiert sie in __main__, warum? Es sollte nie die local_dict verlassen haben ... Argh! Wir zurück zu vollständig importiert httplib. Vielleicht, wenn wir es durch ein Dummy-Objekt ersetzt haben?

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 

Die Module, sterben !!

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

Okay, nachdem alle Versuche, das Beste ist, 2675 (fast + 50%) ab Punkt ... Das ist nur ein Modul aus ... Das ist nicht einmal etwas groß im Innern hat ...

Ok, jetzt ernsthaft, wo ist mein Fehler? Wie kann ich ein Modul entladen und alle Inhalte löschen? Oder haben Pythons Module ein riesiges Speicherleck?

Voll Quelle in einfacher Form zu kopieren: http://gist.github.com/450606

Antwort

17

Python nicht unterstützt Entladen Module.

Wenn jedoch Ihr Programm im Laufe der Zeit keine unbegrenzte Anzahl von Modulen lädt, ist das nicht die Ursache für Ihr Speicherleck. Normalerweise werden Module beim Start geladen und das war's. Ihr Speicherleck liegt wahrscheinlich woanders.

Im unwahrscheinlichen Fall, dass Ihr Programm wirklich eine unbegrenzte Anzahl von Modulen im Laufe der Zeit lädt, sollten Sie wahrscheinlich Ihr Programm neu gestalten. ;-)

+1

Ja, es lädt einigermaßen unbegrenzte Anzahl von Modulen - es ist ein Web-App-Server, der neue Revisionen seines eigenen Quellcodes akzeptiert und neu lädt (das ist ziemlich Standard-Web-Aufgabe). Das Leck ist von der Tatsache, dass alte Code immer noch da draußen im Speicher ist, auch wenn ersetzt, auch wenn nicht erreichbar ... –

+0

Python unterstützt das Entladen von Modulen. Sie sind Müll gesammelt, wie jedes andere Objekt in Python. –

+1

@Slava: Sie können sich den Quellcode von 'mod_python' ansehen, der über einen eigenen Importer verfügt, der das erneute Laden von Modulen ermöglicht, ohne Speicherlecks zu erzeugen. Möglicherweise gibt es dort einen Code, den Sie verwenden könnten. –

0

(Sie sollen versuchen, prägnante Fragen zu schreiben; ich habe nur den Anfang und Mager den Rest lesen.) Ich habe ein einfaches Problem beim Start sehen:

sm = sys.modules.copy() 

Sie eine Kopie gemacht von sys.modules, also hat Ihre Kopie nun einen Verweis auf das Modul - also wird es natürlich nicht gesammelt. Sie können sehen, was sich darauf mit gc.get_referrers bezieht.

Dies funktioniert:

# 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 wird, sobald wir sie von sys.modules entfernen entladen, weil es keine verbleibenden Verweise auf das Modul sind. (Doing nach dem Importieren würde auch funktionieren - Ich habe nur den Import in eine andere Funktion für die Klarheit. Alles, was Sie tun müssen, ist Ihre Referenzen auf sie fallen.)

Jetzt ist es ein wenig schwierig, dies in der Praxis zu tun , wegen zwei Problemen:

  • Um ein Modul zu sammeln, müssen alle Referenzen auf das Modul nicht erreichbar sein (wie beim Sammeln eines Objekts). Das bedeutet, dass alle anderen Module, die es importiert haben, ebenfalls dereferenziert und neu geladen werden müssen.
  • Wenn Sie ein Modul aus sys.modules entfernen, obwohl es immer noch woanders referenziert wird, haben Sie eine ungewöhnliche Situation geschaffen: Das Modul wird immer noch geladen und vom Code verwendet, aber der Modullader weiß nichts mehr davon. Wenn Sie das Modul das nächste Mal importieren, erhalten Sie keinen Verweis auf den vorhandenen (da Sie den Datensatz gelöscht haben), sodass eine zweite koexistierende Kopie des Moduls geladen wird. Dies kann zu schwerwiegenden Konsistenzproblemen führen. Stellen Sie daher sicher, dass keine weiteren Verweise auf das Modul vorhanden sind, bevor Sie es endgültig aus sys.modules entfernen.
  • Es gibt einige knifflige Probleme, um dies im Allgemeinen zu verwenden: Erkennen, welche Module von dem Modul abhängen, das Sie entladen; zu wissen, ob es in Ordnung ist, diese auch zu entladen (hängt stark von Ihrem Anwendungsfall ab); Handhabung von Threading, während Sie all dies untersuchen (siehe imp.acquire_lock), und so weiter.

    Ich könnte mir einen Fall ausdenken, in dem dies nützlich sein könnte, aber die meiste Zeit würde ich empfehlen, die App nur neu zu starten, wenn sich der Code ändert. Sie werden sich wahrscheinlich nur Kopfschmerzen zufügen.

    +8

    Nun, nicht zu Snyde, aber Sie sollten die Frage gelesen haben, oder zumindest das Wort "vollständig" in Titel (oder zumindest die Tags). Das Problem ist nicht, dass ich neu laden will, das Problem ist * Speicherleck * verbunden mit irgendeiner (gelisteten) Art der Modulentfernung (einschließlich der, die du vorgeschlagen hast, die * in meiner Frage aufgelistet sind, zusammen mit Dutzenden anderen). Eigentlich habe ich 'sys.modules.copy()' zu einem sehr späten Zeitpunkt hinzugefügt, das Entfernen ändert nichts (versuchen Sie es selbst). –

    +1

    Die Quelle, um zu versuchen: http://gist.github.com/450606. Versuchen Sie, die Datei sys.modules.copy zu entfernen, und Sie werden feststellen, dass die Objekte immer noch um mehr als 50% zunehmen, obwohl alle Verweise auf das Modul entfernt wurden. –

    +0

    Sehen Sie hier kurz, was falsch ist (mit Ihrem Code): http://gist.github.com/450726. Ich versuche nicht 'sys' zu laden-unload, da wir auf' sys.modules' arbeiten, also benutze ich 'httplib' - Sie können jeden anderen versuchen. –

    3

    Ich bin über Python nicht sicher, aber in anderen Sprachen, tut das Äquivalent von gc.collect() Aufruf nicht Freigabe ungenutzten Speicher - es wird nur, dass der Speicher freigeben, wenn/wenn der Speicher tatsächlich benötigt wird.

    Andernfalls ist es für Python sinnvoll, die Module vorerst im Speicher zu behalten, falls sie erneut geladen werden müssen.

    +0

    Das Problem ist, dass ich sie durch neue Versionen ersetzen muss. Und selbst wenn ich es 1-zu-1 durch ein Modul gleicher Größe ersetze - die Speicherauslastung wächst (Lecks) ... Danke für den Vorschlag. –