Ich habe im folgenden Beispiel einige verwandte Fragen zur Speichernutzung.
Wenn ich im Dolmetscher laufe,
foo = ['bar' for _ in xrange(10000000)]
Der auf meinem Computer verwendete reale Speicher reicht bis zu
80.9mb
. Ich habe dann,del foo
echte Erinnerung geht runter, aber nur zu
30.4mb
. Der Interpreter verwendet die4.4mb
Baseline. Was ist also der Vorteil, wenn kein26mb
Speicher für das Betriebssystem freigegeben wird ? Liegt es daran, dass Python "im Voraus plant" und denkt, dass Sie möglicherweise wieder so viel Speicher verwenden?Warum wird es
50.5mb
speziell veröffentlicht - auf welcher Höhe wird es veröffentlicht?Gibt es eine Möglichkeit, Python zu zwingen, den gesamten verwendeten Speicher freizugeben (wenn Sie wissen, dass Sie nicht mehr so viel Speicher verwenden werden)?
HINWEIS
Diese Frage unterscheidet sich von Wie kann ich explizit Speicher in Python freigeben?
denn diese Frage befasst sich hauptsächlich mit der Erhöhung der Speichernutzung gegenüber dem Ausgangswert, selbst nachdem der Interpreter Objekte über die Speicherbereinigung freigegeben hat (mit gc.collect
oder ohne Verwendung).
quelle
gc.collect
.Antworten:
Der auf dem Heap zugewiesene Speicher kann Hochwassermarken unterliegen. Dies wird durch die internen Optimierungen von Python für die Zuweisung kleiner Objekte (
PyObject_Malloc
) in 4 KiB-Pools erschwert , die für Zuordnungsgrößen bei Vielfachen von 8 Bytes klassifiziert sind - bis zu 256 Bytes (512 Bytes in 3.3). Die Pools selbst befinden sich in 256-KiB-Arenen. Wenn also nur ein Block in einem Pool verwendet wird, wird die gesamte 256-KiB-Arena nicht freigegeben. In Python 3.3 wurde der Allokator für kleine Objekte auf die Verwendung anonymer Speicherzuordnungen anstelle des Heapspeichers umgestellt, sodass die Speicherfreigabe besser sein sollte.Darüber hinaus verwalten die integrierten Typen Freelists von zuvor zugewiesenen Objekten, die möglicherweise den Allokator für kleine Objekte verwenden oder nicht. Der
int
Typ verwaltet eine freie Liste mit seinem eigenen zugewiesenen Speicher, und das Löschen erfordert einen AufrufPyInt_ClearFreeList()
. Dies kann indirekt durch eine vollständige aufgerufen werdengc.collect
.Versuchen Sie es so und sagen Sie mir, was Sie bekommen. Hier ist der Link für psutil.Process.memory_info .
Ausgabe:
Bearbeiten:
Ich habe auf die Messung relativ zur Prozess-VM-Größe umgestellt, um die Auswirkungen anderer Prozesse im System zu eliminieren.
Die C-Laufzeit (z. B. glibc, msvcrt) verkleinert den Heap, wenn der zusammenhängende freie Speicherplatz oben einen konstanten, dynamischen oder konfigurierbaren Schwellenwert erreicht. Mit glibc können Sie dies mit
mallopt
(M_TRIM_THRESHOLD) einstellen. Angesichts dessen ist es nicht verwunderlich, wenn der Haufen um mehr - sogar viel mehr - schrumpft als der Block, den Sie habenfree
.In 3.x
range
wird keine Liste erstellt, sodass im obigen Test keine 10 Millionenint
Objekte erstellt werden. Selbst wenn dies der Fallint
ist , ist der Typ in 3.x im Grunde genommen ein 2.xlong
, der keine Freelist implementiert.quelle
memory_info()
anstelle vonget_memory_info()
undx
ist definiertint
sogar in Python 3 10 ^ 7 s, aber jede ersetzt die letzte in der Schleifenvariablen, sodass nicht alle gleichzeitig existieren .Ich vermute, die Frage, die Sie hier wirklich interessiert, ist:
Nein, da ist kein. Es gibt jedoch eine einfache Problemumgehung: untergeordnete Prozesse.
Wenn Sie 5 Minuten lang 500 MB temporären Speicher benötigen, danach aber noch 2 Stunden laufen müssen und nie wieder so viel Speicher berühren, erstellen Sie einen untergeordneten Prozess, um die speicherintensive Arbeit auszuführen. Wenn der untergeordnete Prozess beendet wird, wird der Speicher freigegeben.
Dies ist nicht ganz trivial und kostenlos, aber es ist ziemlich einfach und billig, was normalerweise gut genug ist, damit sich der Handel lohnt.
Erstens ist der einfachste Weg, einen untergeordneten Prozess zu erstellen, mit
concurrent.futures
(oder für 3.1 und früher denfutures
Backport auf PyPI):Wenn Sie etwas mehr Kontrolle benötigen, verwenden Sie das
multiprocessing
Modul.Die Kosten betragen:
mmap
ped oder anderweitig; die Shared-Memory-APIs inmultiprocessing
; usw.).struct
stecken , -able oder idealerweisectypes
-able).quelle
Eryksun hat Frage 1 beantwortet, und ich habe Frage 3 (das Original Nr. 4) beantwortet, aber jetzt beantworten wir Frage 2:
Worauf es basiert, ist letztendlich eine ganze Reihe von Zufällen in Python
malloc
, die sehr schwer vorherzusagen sind.Erstens, je nachdem, wie Sie den Speicher messen, messen Sie möglicherweise nur Seiten, die tatsächlich dem Speicher zugeordnet sind. In diesem Fall wird der Speicher jedes Mal, wenn eine Seite vom Pager ausgetauscht wird, als "freigegeben" angezeigt, obwohl er nicht freigegeben wurde.
Oder Sie messen verwendete Seiten, die zugewiesene, aber nie berührte Seiten zählen (auf Systemen, die optimistisch zu viel zuweisen, wie z. B. Linux), zugewiesene, aber gekennzeichnete Seiten
MADV_FREE
usw.Wenn Sie wirklich zugewiesene Seiten messen (was eigentlich nicht sehr nützlich ist, aber es scheint das zu sein, worüber Sie fragen) und Seiten wirklich freigegeben wurden, zwei Umstände, unter denen dies passieren kann: Entweder Sie ' Sie haben
brk
oder äquivalent verwendet, um das Datensegment zu verkleinern (heutzutage sehr selten), oder Sie habenmunmap
oder ähnlich verwendet, um ein zugeordnetes Segment freizugeben. (Theoretisch gibt es auch eine kleinere Variante zu letzterem, da es Möglichkeiten gibt, einen Teil eines zugeordneten Segments freizugeben - z. B. ihnMAP_FIXED
für einMADV_FREE
Segment zu stehlen, das Sie sofort entfernen).Aber die meisten Programme ordnen Dinge nicht direkt aus Speicherseiten zu; Sie verwenden einen
malloc
Allokator. Wenn Sie aufrufenfree
, kann der Allokator Seiten nur dann an das Betriebssystem freigeben, wenn Sie geradefree
das letzte Live-Objekt in einem Mapping (oder auf den letzten N Seiten des Datensegments) sind. Ihre Anwendung kann dies auf keinen Fall vernünftigerweise vorhersagen oder sogar im Voraus erkennen, dass dies geschehen ist.CPython macht dies noch komplizierter: Es verfügt über einen benutzerdefinierten 2-Ebenen-Objektzuweiser über einem benutzerdefinierten Speicherzuweiser
malloc
. (Siehe die Quelle Kommentar für eine ausführlichere Erklärung.) Und oben auf , dass auch bei der C - API - Ebene, viel weniger Python, Sie haben nicht einmal direkt steuern , wenn die Top-Level - Objekte freigegeben werden.Wenn Sie also ein Objekt freigeben, woher wissen Sie, ob es Speicher für das Betriebssystem freigibt? Nun, zuerst müssen Sie wissen, dass Sie die letzte Referenz veröffentlicht haben (einschließlich aller internen Referenzen, von denen Sie nichts wussten), damit der GC sie freigeben kann. (Im Gegensatz zu anderen Implementierungen wird mindestens CPython die Zuordnung eines Objekts aufheben, sobald dies zulässig ist.) Dadurch werden normalerweise mindestens zwei Dinge auf der nächsten Ebene freigegeben (z. B. geben Sie für eine Zeichenfolge das
PyString
Objekt und den Zeichenfolgenpuffer frei ).Wenn Sie die Zuordnung eines Objekts aufheben, müssen Sie den internen Status des Objektzuordners sowie dessen Implementierung kennen, um zu wissen, ob die nächste Ebene die Freigabe eines Objektspeicherblocks aufhebt. (Es kann offensichtlich nur passieren, wenn Sie das letzte Element im Block freigeben, und selbst dann kann es nicht passieren.)
Wenn Sie die Zuordnung eines Objektspeicherblocks
free
aufheben, müssen Sie den internen Status des PyMem-Allokators sowie dessen Implementierung kennen, um festzustellen, ob dies einen Aufruf verursacht . (Auch hier müssen Sie die Zuordnung des letzten verwendeten Blocks innerhalb einermalloc
ed-Region aufheben , und selbst dann kann dies möglicherweise nicht passieren.)Wenn Sie tun
free
einemalloc
ed Region, wissen , ob dies ein verursachtmunmap
oder gleichwertig (oderbrk
), müssen Sie den internen Zustand der weißmalloc
, und wie es umgesetzt wird . Und dieser ist im Gegensatz zu den anderen sehr plattformspezifisch. (Und wieder müssen Sie im Allgemeinen die Zuordnung der zuletzt verwendetenmalloc
innerhalb einesmmap
Segments freigeben, und selbst dann kann dies möglicherweise nicht passieren.)Wenn Sie also verstehen möchten, warum genau 50,5 MB veröffentlicht wurden, müssen Sie dies von unten nach oben verfolgen. Warum wurde die
malloc
Zuordnung von Seiten im Wert von 50,5 MB aufgehoben, wenn Sie einen oder mehrerefree
Aufrufe getätigt haben (wahrscheinlich etwas mehr als 50,5 MB)? Sie müssten die Ihrer Plattform lesenmalloc
und dann die verschiedenen Tabellen und Listen durchsuchen, um den aktuellen Status anzuzeigen. (Auf einigen Plattformen werden möglicherweise sogar Informationen auf Systemebene verwendet, die so gut wie unmöglich zu erfassen sind, ohne einen Schnappschuss des Systems zu erstellen, um sie offline zu überprüfen. Glücklicherweise ist dies jedoch normalerweise kein Problem.) Und dann müssen Sie Machen Sie dasselbe auf den 3 darüber liegenden Ebenen.Die einzig nützliche Antwort auf die Frage lautet "Weil".
Sofern Sie keine ressourcenbeschränkte (z. B. eingebettete) Entwicklung durchführen, haben Sie keinen Grund, sich um diese Details zu kümmern.
Und wenn Sie eine ressourcenbeschränkte Entwicklung durchführen, ist es nutzlos, diese Details zu kennen. Sie müssen so ziemlich alle diese Ebenen und insbesondere
mmap
den Speicher, den Sie auf Anwendungsebene benötigen, beenden (möglicherweise mit einem einfachen, gut verstandenen, anwendungsspezifischen Zonenzuweiser dazwischen).quelle
Zunächst möchten Sie möglicherweise Blicke installieren:
Dann führen Sie es im Terminal!
Fügen Sie in Ihrem Python-Code am Anfang der Datei Folgendes hinzu:
Nachdem Sie die Variable "Big" (zum Beispiel: myBigVar) verwendet haben, für die Sie Speicher freigeben möchten, schreiben Sie Folgendes in Ihren Python-Code:
Führen Sie in einem anderen Terminal Ihren Python-Code aus und beobachten Sie im "Blick" -Terminal, wie der Speicher in Ihrem System verwaltet wird!
Viel Glück!
PS Ich nehme an, Sie arbeiten an einem Debian- oder Ubuntu-System
quelle