In einer Nussschale
Finalisierung ist keine einfache Angelegenheit, die von Müllsammlern erledigt werden muss. Die Verwendung mit Referenzzähl-GC ist einfach. Diese GC-Familie ist jedoch häufig unvollständig, sodass Speicherlecks durch explizite Auslösung der Zerstörung und Finalisierung einiger Objekte und Strukturen ausgeglichen werden müssen. Die Verfolgung von Garbage Collectors ist wesentlich effektiver, macht es jedoch schwieriger, zu finalisierende und zu zerstörende Objekte zu identifizieren, als nur den ungenutzten Speicher zu identifizieren. Dies erfordert eine komplexere Verwaltung, kostet Zeit und Platz und ist komplexer die Umsetzung.
Einführung
Ich gehe davon aus, dass Sie nach dem Grund fragen, warum Sprachen mit Speicherbereinigung die Zerstörung / Finalisierung innerhalb des Speicherbereinigungsprozesses nicht automatisch handhaben, wie in der Bemerkung angegeben:
Ich finde es äußerst mangelhaft, dass diese Sprachen das Gedächtnis als die einzige Ressource betrachten, die es wert ist, verwaltet zu werden. Was ist mit Sockets, Dateizugriffsnummern und Anwendungsstatus?
Ich bin mit der akzeptierten Antwort von kdbanman nicht einverstanden . Obwohl die dort genannten Fakten zumeist zutreffend sind, obwohl sie stark auf die Referenzzählung abzielen, glaube ich nicht, dass sie die in der Frage beanstandete Situation richtig erklären.
Ich glaube nicht, dass die in dieser Antwort entwickelte Terminologie ein großes Problem darstellt, und es ist wahrscheinlicher, dass sie die Dinge verwirrt. In der Tat wird die Terminologie, wie dargestellt, hauptsächlich durch die Art und Weise bestimmt, wie die Prozeduren aktiviert werden, und nicht durch die Art und Weise, wie sie ausgeführt werden. Der Punkt ist, dass es in allen Fällen notwendig ist, ein Objekt, das nicht mehr benötigt wird, mit einem Bereinigungsprozess fertigzustellen und alle verwendeten Ressourcen freizugeben, wobei der Speicher nur eine davon ist. Im Idealfall sollte dies alles automatisch erfolgen, wenn das Objekt nicht mehr verwendet werden soll, und zwar mithilfe eines Müllsammlers. In der Praxis kann GC fehlen oder Mängel aufweisen. Dies wird durch die explizite Auslösung durch das Programm für Finalisierung und Rückforderung ausgeglichen.
Explizites Triggern durch das Programm ist ein Problem, da es schwer zu analysierende Programmierfehler ermöglichen kann, wenn ein noch verwendetes Objekt explizit beendet wird.
Daher ist es viel besser, sich auf die automatische Garbage Collection zu verlassen, um Ressourcen zurückzugewinnen. Es gibt jedoch zwei Probleme:
Einige Speicherbereinigungstechniken ermöglichen Speicherlecks, die eine vollständige Rückgewinnung von Ressourcen verhindern. Dies ist allgemein für die Referenzzählung von GC-Daten bekannt, kann jedoch für andere GC-Techniken angezeigt werden, wenn einige Datenorganisationen ohne Sorgfalt verwendet werden (hier nicht besprochener Punkt).
Während die GC-Technik möglicherweise die Identifizierung von nicht mehr verwendeten Speicherressourcen erleichtert, ist das Finalisieren der darin enthaltenen Objekte möglicherweise nicht einfach, und dies erschwert das Problem, andere von diesen Objekten verwendete Ressourcen zurückzugewinnen, was häufig der Zweck der Finalisierung ist.
Schließlich wird häufig vergessen, dass GC-Zyklen durch irgendetwas ausgelöst werden können, nicht nur durch Speichermangel, wenn die richtigen Hooks bereitgestellt werden und die Kosten für einen GC-Zyklus als wertvoll erachtet werden. Daher ist es vollkommen in Ordnung, eine GC einzuleiten, wenn irgendeine Art von Ressource fehlt, in der Hoffnung, eine zu befreien.
Referenzzählung Müllsammler
Die Referenzzählung ist eine schwache Müllsammeltechnik , die Zyklen nicht richtig handhabt. Es wäre in der Tat schwach, veraltete Strukturen zu zerstören und andere Ressourcen zurückzugewinnen, nur weil es schwach ist, Speicher zurückzugewinnen. Finalizer können jedoch am einfachsten mit einem Garbage Collector (GC) mit Referenzzählung verwendet werden, da ein GC mit Referenzzählung eine Struktur zurückerlangt, wenn seine Referenzzählung auf 0 abfällt. Zu diesem Zeitpunkt ist seine Adresse zusammen mit seinem Typ entweder statisch bekannt oder dynamisch. Daher ist es möglich, den Speicher genau nach dem Anwenden des richtigen Finalizers und dem rekursiven Aufrufen des Prozesses auf alle spitzen Objekte (möglicherweise über die Finalisierungsprozedur) wiederzugewinnen.
Zusammenfassend lässt sich sagen, dass die Finalisierung mit Ref Counting GC leicht zu implementieren ist, jedoch unter der "Unvollständigkeit" dieses GC leidet, und zwar aufgrund kreisförmiger Strukturen, und zwar in genau demselben Ausmaß, unter dem die Speicherfreigabe leidet. Mit anderen Worten, mit der Referenzanzahl wird der Speicher genauso schlecht verwaltet wie andere Ressourcen wie Sockets, Dateihandles usw.
Tatsächlich kann die Unfähigkeit von Ref Count GC, Schleifenstrukturen (im Allgemeinen) zurückzugewinnen, als Speicherverlust angesehen werden . Sie können nicht von allen GCs erwarten, dass sie Speicherlecks vermeiden. Dies hängt vom GC-Algorithmus und von den dynamisch verfügbaren Typstrukturinformationen ab (z. B. bei
konservativem GC ).
Müllsammler aufspüren
Die leistungsstärkere Familie von GC ohne solche Lecks ist die Verfolgungsfamilie , die die aktiven Teile des Speichers ausgehend von gut identifizierten Stammzeigern untersucht. Alle Teile des Speichers, die in diesem Ablaufverfolgungsprozess nicht besucht werden (die tatsächlich auf verschiedene Arten zerlegt werden können, aber ich muss sie vereinfachen), sind nicht verwendete Teile des Speichers, die auf diese Weise zurückgewonnen werden können 1 . Diese Kollektoren rufen alle Speicherteile ab, auf die das Programm nicht mehr zugreifen kann, unabhängig davon, was es tut. Es werden kreisförmige Strukturen wiederhergestellt, und die fortgeschritteneren GC basieren auf einigen Variationen dieses Paradigmas, die manchmal hochentwickelt sind. In einigen Fällen kann es mit der Referenzzählung kombiniert werden und seine Schwächen ausgleichen.
Ein Problem ist, dass Ihre Aussage (am Ende der Frage):
Sprachen, die eine automatische Speicherbereinigung anbieten, scheinen die Hauptkandidaten für die Zerstörung / Finalisierung von Objekten zu sein, da sie mit 100% iger Sicherheit wissen, wann ein Objekt nicht mehr verwendet wird.
ist für die Rückverfolgung von Sammlern technisch falsch .
Mit 100% iger Sicherheit ist bekannt, welche Teile des Speichers nicht mehr verwendet werden . (Genauer gesagt, sie sind nicht mehr zugänglich , da einige Teile, die gemäß der Logik des Programms nicht mehr verwendet werden können, weiterhin als verwendet betrachtet werden, wenn das Programm noch einen nutzlosen Zeiger auf sie enthält Daten.) Aber weitere Verarbeitung und geeignete Strukturen sind erforderlich, um zu wissen, welche nicht verwendeten Objekte in diesen jetzt nicht verwendeten Teilen des Speichers gespeichert wurden . Dies kann aus dem, was von dem Programm bekannt ist, nicht bestimmt werden, da das Programm nicht länger mit diesen Teilen des Speichers verbunden ist.
Nach einem Durchlauf der Speicherbereinigung verbleiben also Fragmente des Speichers, die Objekte enthalten, die nicht mehr verwendet werden. Es ist jedoch a priori nicht möglich, diese Objekte zu ermitteln, um die korrekte Finalisierung anzuwenden. Wenn es sich bei dem Ablaufverfolgungskollektor um den Mark-and-Sweep-Typ handelt, können einige der Fragmente Objekte enthalten, die bereits in einem vorherigen GC-Durchgang finalisiert wurden, seitdem jedoch aus Fragmentierungsgründen nicht mehr verwendet wurden. Dies kann jedoch mit erweiterter expliziter Typisierung behoben werden.
Während ein einfacher Kollektor diese Speicherfragmente nur ohne weiteres zurückerobern würde, erfordert die Finalisierung einen bestimmten Durchgang, um den nicht verwendeten Speicher zu untersuchen, die darin enthaltenen Objekte zu identifizieren und Finalisierungsverfahren anzuwenden. Eine solche Untersuchung erfordert jedoch die Bestimmung des Objekttyps, der dort gespeichert wurde, und die Typbestimmung ist auch erforderlich, um gegebenenfalls die ordnungsgemäße Finalisierung durchzuführen.
Dies bedeutet zusätzliche Kosten in der GC-Zeit (der zusätzliche Durchlauf) und möglicherweise zusätzliche Speicherkosten, um während dieses Durchlaufs die richtigen Typinformationen durch verschiedene Techniken verfügbar zu machen. Diese Kosten können erheblich sein, da häufig nur wenige Objekte finalisiert werden sollen, während der zeitliche und räumliche Aufwand alle Objekte betreffen kann.
Ein weiterer Punkt ist, dass der zeitliche und räumliche Aufwand die Ausführung von Programmcode und nicht nur die GC-Ausführung betreffen kann.
Ich kann keine genauere Antwort geben und auf bestimmte Fragen hinweisen, da ich die Besonderheiten vieler der von Ihnen aufgelisteten Sprachen nicht kenne. Im Fall von C ist die Typisierung ein sehr schwieriges Thema, das zur Entwicklung konservativer Sammler führt. Ich vermute, dass dies auch C ++ betrifft, aber ich bin kein Experte für C ++. Dies scheint von Hans Boehm bestätigt zu werden , der einen Großteil der Forschung zur konservativen GC durchgeführt hat. Conservative GC kann nicht systematisch den gesamten nicht verwendeten Speicher zurückfordern, da möglicherweise keine genauen Typinformationen zu Daten vorliegen. Aus dem gleichen Grund wäre es nicht möglich, Finalisierungsverfahren systematisch anzuwenden.
So ist es möglich, das zu tun, wonach Sie fragen, wie Sie es aus einigen Sprachen kennen. Aber es ist nicht kostenlos. Abhängig von der Sprache und ihrer Implementierung kann dies auch dann Kosten verursachen, wenn Sie die Funktion nicht verwenden. Verschiedene Techniken und Kompromisse können in Betracht gezogen werden, um diese Probleme anzugehen, aber das würde den Rahmen einer angemessenen Antwort sprengen.
1 - Dies ist eine abstrakte Darstellung der Ablaufverfolgungssammlung (die sowohl das Kopieren als auch das Mark-and-Sweep-GC umfasst). Die Dinge variieren je nach Typ des Ablaufverfolgungssammlers, und das Erkunden des nicht verwendeten Teils des Speichers ist unterschiedlich, je nachdem, ob Kopieren oder Markieren und Sweep wird verwendet.
finalize
/destroy
eine Lüge ist? Es gibt keine Garantie, dass es jemals ausgeführt wird. Und selbst wenn Sie nicht wissen, wann (bei gegebener automatischer Garbage Collection) und falls erforderlich, immer noch Kontext vorhanden ist (möglicherweise wurde er bereits gesammelt). Es ist also sicherer, einen konsistenten Zustand auf andere Weise sicherzustellen, und man möchte den Programmierer möglicherweise dazu zwingen.