5 Stimmen

Aufteilen und Überwinden von großen Objekten für GC-Leistung

Bei meiner Arbeit diskutieren wir verschiedene Ansätze, um eine große Menge an verwaltetem Speicher (50-100 MB) zu bereinigen, wobei zwei Ansätze auf dem Tisch liegen (sprich: zwei erfahrene Entwickler können sich nicht einigen) und der Rest des Teams ist sich nicht sicher, welcher Ansatz wünschenswerter ist: Leistung oder Wartbarkeit.

Die gesammelten Daten bestehen aus vielen kleinen Objekten, ca. 30000, die wiederum andere Objekte enthalten; alle Objekte werden verwaltet. Es gibt viele Verweise zwischen diesen Objekten, einschließlich Ereignis-Handlern, aber nicht auf externe Objekte. Wir bezeichnen diese große Gruppe von Objekten und Verweisen als eine einzige Einheit, die als Blob bezeichnet wird.

Ansatz #1: Stellen Sie sicher, dass alle Verweise auf Objekte im Blob getrennt sind, und lassen Sie die GC den Blob und alle Verbindungen verarbeiten.

Ansatz #2: Implementieren Sie IDisposable für diese Objekte, rufen Sie dispose für diese Objekte auf und setzen Sie Verweise auf Nothing und remove-Handler.

Die Theorie hinter dem zweiten Ansatz ist, dass die großen, langlebigen Objekte länger brauchen, um im GC bereinigt zu werden. Wenn man also die großen Objekte in kleinere Häppchen zerlegt, kann der Garbage Collector sie schneller verarbeiten, was einen Leistungsgewinn bedeutet.

Ich denke, die grundlegende Frage ist die folgende: Ist es besser, große Gruppen miteinander verbundener Objekte zu zerlegen, um die Daten für die Garbage Collection zu optimieren, oder ist es besser, sie zusammenzuhalten und sich darauf zu verlassen, dass die Garbage Collection-Algorithmen die Daten für Sie verarbeiten?

Ich habe das Gefühl, dass dies ein Fall von Voroptimierung ist, aber ich kenne den GC nicht gut genug, um zu wissen, was ihm hilft oder ihn behindert.

Editar: Der "Blob" des Speichers ist nicht ein einziges großes Objekt, sondern viele kleine Objekte, die einzeln zugewiesen werden.

Ein wenig mehr Hintergrund, falls es hilfreich ist. Wir hatten "Lecks", da die Objekte nicht GCed wurden. Beide Ansätze lösen das Leck-Problem, aber an diesem Punkt ist es eine Debatte darüber, welcher besser geeignet ist.

1voto

jpalecek Punkte 45829

Ansatz #2: Implementieren Sie IDisposable auf diese Objekte und rufen dann dispose für diese Objekte auf und setzen Referenzen auf Nothing setzen und Handler entfernen.

...

Die Theorie hinter dem zweiten Ansatz ist, dass die großen, langlebigen Objekte länger für die Bereinigung in der GC. Indem man also die großen Objekte in kleinere, mundgerechte Häppchen zerteilt, wird der Garbage Collector sie schneller verarbeiten schneller, was einen Leistungsgewinn bedeutet.

Ich denke, das stimmt nicht; die Kosten der Garbage Collectors hängen in der Regel von der Anzahl der lebenden Objekte und ihrer Referenzen und der Anzahl der toten Objekte ab (je nach Art der GC). Sobald man ein Objekt (oder mehrere Objekte) nicht mehr braucht und die Referenzpfade von Root-Objekten zu ihm/ihnen gekappt hat, spielt die Anzahl der Referenzen zwischen den "Garbage"-Objekten keine Rolle mehr. Ich würde also sagen, stellen Sie einfach sicher, dass es keine baumelnden Referenzen von außerhalb der "Blobs" gibt, dann ist alles in Ordnung.

0voto

supercat Punkte 72939

Jedes Objekt, das einen Finalizer hat, sollte nach Möglichkeit entsorgt werden, bevor es aufgegeben wird. Das Verlassen eines Objekts mit einem Finalizer sollte aus Sicht der GC-Leistung als der schlimmste Fall angesehen werden.

Darüber hinaus ist es auch ohne Finalizer möglich, Szenarien zu konstruieren, in denen es besser ist, einen großen Blob einfach vom Rest der Welt zu trennen und ihn sterben zu lassen, und es ist möglich, Szenarien zu konstruieren, in denen es besser ist, das große Objekt auseinander zu brechen. Im Allgemeinen wäre es optimal, das große Objekt einfach sterben zu lassen, abgesehen von zwei Vorbehalten, die das Aufbrechen begünstigen können:

  1. Wenn Teile des Blobs langlebig sind, ist es unwahrscheinlich, dass irgendein Teil davon, egal wie kürzlich zugewiesen, vor der nächsten Level-2-Garbage-Collection eingesammelt werden kann. Wenn man dagegen alle Referenzen, die den Blob zusammenhalten, auflöst, können die Teile, die erst kürzlich zugewiesen wurden, für eine Level-0- oder Level-1-Sammlung in Frage kommen. Wenn genügend Objekte innerhalb des Blobs vergleichsweise neu sind, kann der Aufwand, der erforderlich ist, um die Referenzen, die von den langlebigeren Teilen gehalten werden, zu zerstören, geringer sein als die Arbeit, die der GC ansonsten leisten würde, um die neueren Objekte bis zur nächsten Level-2-Sammlung zu behalten.
  2. Wenn ein Verweis auf einen Teil des Blobs existiert und der Blob intakt bleibt, kann dieser Verweis den gesamten Blob am Leben erhalten. Wird der Blob dagegen auseinandergesprengt, kann der Verweis nur einen kleinen Teil davon am Leben erhalten. Das kann eine gute oder schlechte Sache sein. Wenn die Alternative darin besteht, den gesamten Blob am Leben zu erhalten, ist es wahrscheinlich besser, nur einen kleinen Teil am Leben zu erhalten. Andererseits, wenn es darum geht, ein Problem zu finden und zu beheben (Beseitigung der Streureferenz) oder das Problem nicht zu finden, könnte Ersteres besser sein.

Ich persönlich mag es nicht, wenn Ereignisse in Fällen aufgegeben werden, in denen ein externes Objekt einen Verweis auf den Herausgeber enthalten könnte. Proaktives Aufräumen scheint mir die bessere Angewohnheit zu sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X