Ich habe ein Skript mit langer Laufzeit, das, wenn es lange genug ausgeführt wird, den gesamten Speicher auf meinem System belegt.
Ohne näher auf das Skript einzugehen, habe ich zwei Fragen:
Schauen Sie sich diesen Artikel an: Tracing python Memory Leaks
Beachten Sie auch, dass im Garbage Collection-Modul tatsächlich Debug-Flags gesetzt sein können. Schaue auf die set_debug
Funktion. Schauen Sie sich außerdem dieser Code von Gnibbler an, um die Objekttypen zu bestimmen, die nach einem Aufruf erstellt wurden.
Ich habe die meisten zuvor erwähnten Optionen ausprobiert, aber dieses kleine und intuitive Paket als das beste befunden: pympler
Es ist ganz einfach, Objekte zu verfolgen, die nicht vom Müll gesammelt wurden. Schauen Sie sich dieses kleine Beispiel an:
paket installieren über pip install pympler
from pympler.tracker import SummaryTracker
tracker = SummaryTracker()
# ... some code you want to investigate ...
tracker.print_diff()
Die Ausgabe zeigt Ihnen alle hinzugefügten Objekte sowie den von ihnen belegten Speicher.
Beispielausgabe:
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
Dieses Paket bietet eine Reihe weiterer Funktionen. Überprüfen Sie Pymplers Dokumentation , insbesondere den Abschnitt Identifizierung von Speicherlecks .
Lassen Sie mich empfehlen mem_top Tool,
das hat mir geholfen, ein ähnliches Problem zu lösen.
In einem Python Programm werden die häufigsten Verdächtigen für Speicherverluste sofort angezeigt.
Tracemalloc-Modul wurde als integriertes Modul ab Python 3.4 integriert und ist anscheinend auch für frühere Versionen von Python verfügbar = as eine Drittanbieter-Bibliothek (habe es jedoch nicht getestet).
Dieses Modul kann die genauen Dateien und Zeilen ausgeben, die den meisten Speicher zugewiesen haben. IMHO, diese Information ist unendlich wertvoller als die Anzahl der zugewiesenen Instanzen für jeden Typ (was 99% der Zeit eine Menge Tupel ergibt, was ein Hinweis ist, aber in den meisten Fällen kaum hilft).
Ich empfehle Ihnen, tracemalloc in Kombination mit Pyrasit zu verwenden. Wenn Sie das Top-10-Snippet in einer Pyrasit-Shell 9-mal von 10 ausführen, erhalten Sie genügend Informationen und Hinweise, um das Leck innerhalb von 10 Minuten zu beheben. Wenn Sie die Ursache des Lecks jedoch immer noch nicht finden können, gibt Ihnen Pyrasit-Shell in Kombination mit den anderen in diesem Thread erwähnten Tools wahrscheinlich noch weitere Hinweise. Sie sollten sich auch alle zusätzlichen Hilfsprogramme ansehen, die Pyrasite zur Verfügung stellt (z. B. Memory Viewer).
Sie sollten sich besonders Ihre globalen oder statischen Daten (Long Living Data) ansehen.
Wenn diese Daten uneingeschränkt wachsen, kann es auch in Python zu Problemen kommen.
Der Garbage Collector kann nur Daten sammeln, auf die nicht mehr verwiesen wird. Ihre statischen Daten können jedoch Datenelemente verbinden, die freigegeben werden sollen.
Ein weiteres Problem können Speicherzyklen sein, aber zumindest in der Theorie sollte der Garbage Collector Zyklen finden und eliminieren - zumindest solange sie nicht auf einige Daten mit langer Lebensdauer angewiesen sind.
Welche Arten von Daten mit langer Lebensdauer sind besonders problematisch? Sehen Sie sich Listen und Wörterbücher genau an - sie können unbegrenzt wachsen. In Wörterbüchern sehen Sie möglicherweise gar keine Probleme, da beim Zugriff auf Dikte die Anzahl der Schlüssel im Wörterbuch für Sie möglicherweise nicht von großer Bedeutung ist ...
Zum Erkennen und Lokalisieren von Speicherlecks bei lang laufenden Prozessen, z. In Produktionsumgebungen können Sie jetzt stackimpact verwenden. Es wird tracemalloc darunter verwendet. Weitere Infos in dieser Beitrag .
Wir sind uns nicht sicher über "Best Practices" für Speicherverluste in Python, aber python sollte seinen eigenen Speicher durch den Garbage Collector löschen. Daher würde ich hauptsächlich nach einer kurzen zirkulären Liste suchen, da sie wird nicht vom Müllmann abgeholt.
Achten Sie bei Best Practices auf rekursive Funktionen. In meinem Fall stieß ich auf Probleme mit der Rekursion (wo es keine geben musste). Ein vereinfachtes Beispiel von dem, was ich tat:
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()
wenn Sie auf diese rekursive Weise arbeiten, wird die Garbage Collection nicht ausgelöst und die Reste der Funktion werden gelöscht, sodass die Speichernutzung mit jedem Mal zunimmt.
Meine Lösung bestand darin, den rekursiven Aufruf aus my_function () zu ziehen und main () zu behandeln, wenn er erneut aufgerufen werden soll. Auf diese Weise endet die Funktion auf natürliche Weise und bereinigt sich selbst.
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()
Dies ist keineswegs ein vollständiger Ratschlag. Beim Schreiben mit dem Gedanken, zukünftige Speicherverluste (Schleifen) zu vermeiden, ist jedoch zu beachten, dass alles, was einen Verweis auf einen Rückruf akzeptiert, diesen Rückruf als schwachen Verweis speichern sollte.