Frage

Python verwendet die Referenzzählmethode, um die Objektlebensdauer zu verwalten.Ein Gegenstand, der keinen Nutzen mehr hat, wird also sofort zerstört.

Aber in Java zerstört der GC (Garbage Collector) Objekte, die zu einem bestimmten Zeitpunkt nicht mehr verwendet werden.

Warum wählt Java diese Strategie und welchen Nutzen hat sie?

Ist das besser als der Python-Ansatz?

War es hilfreich?

Lösung

Die Verwendung der Referenzzählung hat Nachteile.Einer der am häufigsten genannten sind Zirkelverweise:Angenommen, A verweist auf B, B verweist auf C und C verweist auf B.Wenn A seine Referenz auf B fallen lässt, haben sowohl B als auch C immer noch einen Referenzzähler von 1 und werden bei der herkömmlichen Referenzzählung nicht gelöscht.CPython (die Referenzzählung ist nicht Teil von Python selbst, sondern Teil der C-Implementierung davon) fängt Zirkelverweise mit einer separaten Garbage-Collection-Routine ab, die regelmäßig ausgeführt wird ...

Ein weiterer Nachteil:Die Referenzzählung kann die Ausführung verlangsamen.Jedes Mal, wenn ein Objekt referenziert und dereferenziert wird, muss der Interpreter/die VM prüfen, ob der Zähler auf 0 gesunken ist (und dann die Zuordnung aufheben, wenn dies der Fall ist).Garbage Collection muss dies nicht tun.

Außerdem kann die Garbage Collection in einem separaten Thread durchgeführt werden (obwohl dies etwas knifflig sein kann).Auf Maschinen mit viel RAM und bei Prozessen, die den Speicher nur langsam verbrauchen, möchten Sie möglicherweise überhaupt keine GC durchführen!Die Referenzzählung wäre dort in Bezug auf die Leistung ein kleiner Nachteil ...

Andere Tipps

Tatsächlich handelt es sich bei der Referenzzählung und den von der Sun JVM verwendeten Strategien um unterschiedliche Arten von Garbage-Collection-Algorithmen.

Um tote Objekte aufzuspüren, gibt es zwei grundsätzliche Ansätze:Nachverfolgung und Referenzzählung.Beim Tracing geht der GC von den „Wurzeln“ aus – Dingen wie Stack-Referenzen – und verfolgt alle erreichbaren (lebenden) Objekte.Alles, was nicht erreichbar ist, gilt als tot.Bei der Referenzzählung wird jedes Mal, wenn eine Referenz geändert wird, die Anzahl der beteiligten Objekte aktualisiert.Jedes Objekt, dessen Referenzzähler auf Null gesetzt wird, gilt als tot.

Grundsätzlich gibt es bei allen GC-Implementierungen Kompromisse, aber die Ablaufverfolgung eignet sich normalerweise für einen hohen Durchsatz (d. h.schneller) Betrieb, hat aber längere Pausenzeiten (größere Lücken, in denen die Benutzeroberfläche oder das Programm einfrieren kann).Die Referenzzählung kann in kleineren Abschnitten erfolgen, ist aber insgesamt langsamer.Dies bedeutet möglicherweise weniger Einfrierungen, aber insgesamt eine schlechtere Leistung.

Darüber hinaus erfordert ein Referenzzähl-GC einen Zyklusdetektor, um alle Objekte in einem Zyklus zu bereinigen, die nicht allein durch ihre Referenzzählung erfasst werden.Perl 5 hatte in seiner GC-Implementierung keinen Zyklusdetektor und konnte zyklischen Speicher verlieren.

Es wurden auch Untersuchungen durchgeführt, um das Beste aus beiden Welten herauszuholen (geringe Pausenzeiten, hoher Durchsatz):http://cs.anu.edu.au/~Steve.Blackburn/pubs/papers/urc-oopsla-2003.pdf

Darren Thomas gibt eine gute Antwort.Ein großer Unterschied zwischen den Java- und Python-Ansätzen besteht jedoch darin, dass bei der Referenzzählung im allgemeinen Fall (keine Zirkelverweise) Objekte sofort und nicht zu einem unbestimmten späteren Zeitpunkt bereinigt werden.

Ich kann zum Beispiel schlampigen, nicht portablen Code in CPython schreiben, z

def parse_some_attrs(fname):
    return open(fname).read().split("~~~")[2:4]

und der Dateideskriptor für die Datei, die ich geöffnet habe, wird sofort bereinigt, denn sobald der Verweis auf die geöffnete Datei verschwindet, wird die Datei durch Müll gesammelt und der Dateideskriptor wird freigegeben.Wenn ich Jython oder IronPython oder möglicherweise PyPy ausführe, wird der Garbage Collector natürlich erst viel später ausgeführt.Möglicherweise gehen mir zuerst die Dateideskriptoren aus und mein Programm stürzt ab.

Sie SOLLTEN also Code schreiben, der so aussieht

def parse_some_attrs(fname):
    with open(fname) as f:
        return f.read().split("~~~")[2:4]

Aber manchmal verlassen sich die Leute gerne auf die Referenzzählung, um immer Ressourcen freizugeben, weil Ihr Code dadurch manchmal etwas kürzer wird.

Ich würde sagen, dass der beste Garbage Collector derjenige mit der besten Leistung ist. Dies scheinen derzeit die generationsübergreifenden Garbage Collectors im Java-Stil zu sein, die in einem separaten Thread ausgeführt werden können und über all diese verrückten Optimierungen usw. verfügen.Die Unterschiede in der Art und Weise, wie Sie Ihren Code schreiben, sollten vernachlässigbar sein und im Idealfall nicht vorhanden sein.

Ich denke, der Artikel „Java-Theorie und -Praxis:Eine kurze Geschichte der Müllabfuhr" von IBM sollte Ihnen dabei helfen, einige Ihrer Fragen zu klären.

Die Garbage Collection ist schneller (zeiteffizienter) als die Referenzzählung, wenn Sie über genügend Speicher verfügen.Beispielsweise durchläuft ein kopierender GC die „lebenden“ Objekte und kopiert sie in einen neuen Bereich und kann alle „toten“ Objekte in einem Schritt zurückgewinnen, indem er einen gesamten Speicherbereich markiert.Das ist sehr effizient, Wenn Du hast genug Speicher.Generationensammlungen nutzen das Wissen, dass „die meisten Objekte jung sterben“;Oftmals müssen nur wenige Prozent der Objekte kopiert werden.

[Dies ist auch der Grund, warum gc schneller sein kann als malloc/free]

Die Referenzzählung ist viel platzsparender als die Garbage Collection, da sie den Speicher in dem Moment zurückgewinnt, in dem er nicht mehr erreichbar ist.Das ist praktisch, wenn Sie Finalizer an Objekte anhängen möchten (z. B.um eine Datei zu schließen, sobald das Dateiobjekt nicht mehr erreichbar ist).Ein Referenzzählsystem kann auch dann funktionieren, wenn nur wenige Prozent des Speichers frei sind.Der Verwaltungsaufwand, der durch das Erhöhen und Erniedrigen von Zählern bei jeder Zeigerzuweisung entsteht, kostet jedoch viel Zeit, und es ist immer noch eine Art Garbage Collection erforderlich, um Zyklen zurückzugewinnen.

Der Kompromiss ist also klar:Wenn Sie in einer Umgebung mit begrenztem Speicher arbeiten müssen oder präzise Finalizer benötigen, verwenden Sie die Referenzzählung.Wenn Sie über genügend Speicher verfügen und die Geschwindigkeit benötigen, verwenden Sie die Garbage Collection.

Ein großer Nachteil von Javas Tracing-GC besteht darin, dass es von Zeit zu Zeit „die Welt anhält“ und die Anwendung für relativ lange Zeit einfriert, um einen vollständigen GC durchzuführen.Wenn der Heap groß und der Objektbaum komplex ist, friert er für einige Sekunden ein.Außerdem besucht jeder vollständige GC immer wieder den gesamten Objektbaum, was wahrscheinlich ziemlich ineffizient ist.Ein weiterer Nachteil der Art und Weise, wie Java GC ausführt, besteht darin, dass Sie dem JVM mitteilen müssen, welche Heap-Größe Sie wünschen (wenn die Standardeinstellung nicht gut genug ist).Die JVM leitet aus diesem Wert mehrere Schwellenwerte ab, die den GC-Prozess auslösen, wenn sich zu viel Müll im Heap ansammelt.

Ich gehe davon aus, dass dies tatsächlich die Hauptursache für das ruckartige Gefühl von Android (basierend auf Java) ist, selbst auf den teuersten Mobiltelefonen im Vergleich zur Laufruhe von iOS (basierend auf ObjectiveC und mit RC).

Ich würde gerne eine JVM-Option sehen, um die RC-Speicherverwaltung zu aktivieren und GC vielleicht nur als letzten Ausweg laufen zu lassen, wenn kein Speicher mehr übrig ist.

Die neueste Sun Java VM verfügt tatsächlich über mehrere GC-Algorithmen, die Sie optimieren können.In den Java-VM-Spezifikationen wurde absichtlich darauf verzichtet, das tatsächliche GC-Verhalten anzugeben, um unterschiedliche (und mehrere) GC-Algorithmen für verschiedene VMs zu ermöglichen.

Für alle Leute, die den „Stop-the-World“-Ansatz des Standardverhaltens von Sun Java VM GC nicht mögen, gibt es beispielsweise VMs wie WebSphere Real Time von IBM Dadurch können Echtzeitanwendungen auf Java ausgeführt werden.

Da die Java-VM-Spezifikation öffentlich verfügbar ist, hindert (theoretisch) niemand daran, eine Java-VM zu implementieren, die den GC-Algorithmus von CPython verwendet.

In einer Umgebung mit mehreren Threads ist es besonders schwierig, Referenzen effizient zu zählen.Ich weiß nicht, wie Sie überhaupt damit anfangen würden, ohne sich auf hardwaregestützte Transaktionen oder ähnliche (derzeit) ungewöhnliche atomare Anweisungen einzulassen.

Die Referenzzählung ist einfach zu implementieren.JVMs haben viel Geld in konkurrierende Implementierungen gesteckt, daher sollte es nicht überraschen, dass sie sehr gute Lösungen für sehr schwierige Probleme implementieren.Es wird jedoch immer einfacher, Ihre Lieblingssprache bei der JVM gezielt anzusprechen.

Spät im Spiel, aber ich denke, ein wichtiger Grund für RC in Python ist seine Einfachheit.Sieh dir das an E-Mail von Alex Martelli, Zum Beispiel.

(Ich konnte keinen Link außerhalb des Google-Cache finden, die E-Mail stammt vom 13. Oktober 2005 auf der Python-Liste).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top