Wann ist es eine gute Idee, die Garbage Collection zu erzwingen?

135

Ich habe gerade eine Frage gelesen , in der es darum geht, den C # -Müllsammler zum Ausführen zu zwingen, bei der fast jede einzelne Antwort gleich ist: Sie können es tun, aber Sie sollten es nicht - mit Ausnahme einiger sehr seltener Fälle . Leider geht dort niemand auf solche Fälle ein.

Können Sie mir sagen, in welchem ​​Szenario es eigentlich eine gute oder vernünftige Idee ist, die Garbage Collection zu erzwingen?

Ich frage nicht nach C # -Fällen, sondern nach allen Programmiersprachen mit Garbage Collector. Ich weiß, dass Sie GC nicht für alle Sprachen wie Java erzwingen können, aber nehmen wir an, Sie können es.

Omega
quelle
17
"sondern alle Programmiersprachen, die einen Garbage Collector haben" Verschiedene Sprachen (oder besser gesagt verschiedene Implementierungen ) verwenden unterschiedliche Methoden für die Garbage Collection, so dass es unwahrscheinlich ist, dass Sie eine Einheitsregel finden.
Colonel Zweiunddreißig
4
@Doval Wenn Sie sich unter Echtzeitbeschränkungen befinden und der GC keine passenden Garantien bietet, befinden Sie sich zwischen einem Stein und einem harten Ort. Es kann unerwünschte Pausen reduzieren und nichts tun, aber nach dem, was ich gehört habe, ist es "einfacher", eine Zuordnung im normalen Betriebsablauf zu vermeiden.
3
Ich hatte den Eindruck, dass Sie niemals eine GC-Sprache verwenden würden, wenn Sie harte Echtzeit-Fristen erwarten würden.
GregRos
4
Ich kann nicht sehen, wie Sie dies auf eine nicht VM-spezifische Weise beantworten können. Relevant für 32-Bit-Prozesse, relevant für 64-Bit-Prozesse. .NET JVM und für die High-End
Rwong
3
@ DavidConrad Sie können es in C # erzwingen. Daher die Frage.
Omega

Antworten:

127

Sie können wirklich keine pauschalen Aussagen über die angemessene Verwendung aller GC-Implementierungen machen. Sie variieren stark. Also spreche ich mit dem .NET, auf das Sie sich ursprünglich bezogen haben.

Sie müssen das Verhalten des GC ziemlich genau kennen, um dies mit einer Logik oder einem Grund zu tun.

Der einzige Rat, den ich zum Sammeln geben kann, ist: Tu es niemals.

Wenn Sie die komplizierten Details des GC wirklich kennen, brauchen Sie meinen Rat nicht, es spielt also keine Rolle. Wenn Sie es noch nicht zu 100% sicher wissen, kann es hilfreich sein, online nachzuschauen und eine Antwort wie die folgende zu finden: Sie sollten GC.Collect nicht anrufen oder alternativ: Sie sollten sich mit den Einzelheiten der Funktionsweise des GC vertraut machen innen und außen, und nur dann werden Sie wissen die Antwort .

Es gibt einen sicheren Ort, an dem es sinnvoll ist, GC.Collect zu verwenden :

GC.Collect ist eine API, die Sie zum Erstellen von Zeitprofilen für bestimmte Dinge verwenden können. Sie können einen Algorithmus profilieren, sammeln und einen anderen Algorithmus unmittelbar danach profilieren, wenn Sie wissen, dass die GC der ersten Algo nicht während der zweiten Algo aufgetreten ist, wodurch die Ergebnisse verzerrt werden.

Diese Art der Profilerstellung ist das einzige Mal, dass ich jedem empfehlen würde, manuell Daten zu sammeln.


Auf jeden Fall ein erfundenes Beispiel

Ein möglicher Anwendungsfall ist, dass wenn Sie sehr große Objekte laden, diese in den Large Object Heap gelangen, der direkt an Gen 2 weitergeleitet wird. Gen 2 ist jedoch auch für langlebige Objekte gedacht, da es weniger häufig erfasst wird. Wenn Sie wissen, dass Sie kurzlebige Objekte aus irgendeinem Grund in Gen 2 laden, können Sie sie schneller entfernen, um Ihr Gen 2 kleiner und seine Sammlungen schneller zu halten.

Dies ist das beste Beispiel, das ich mir einfallen lassen könnte, und es ist nicht gut - der LOH-Druck, den Sie hier aufbauen, würde zu häufigeren Sammlungen führen, und Sammlungen sind so häufig, wie es ist schnell wie Sie es mit temporären Objekten ausblasen. Ich traue mich einfach nicht zu, eine bessere Erfassungshäufigkeit als die GC selbst anzunehmen - abgestimmt von Leuten, die weitaus schlauer sind als ich.


Sprechen wir also über einige der Semantiken und Mechanismen in .NET GC ... oder ..

Alles , was ich zu wissen glaube , über .NET GC

Bitte, wer hier Fehler findet - korrigiert mich. Ein Großteil der GCs ist als schwarze Magie bekannt, und obwohl ich versucht habe, Einzelheiten, über die ich mir nicht sicher war, auszulassen, habe ich wahrscheinlich immer noch einiges falsch gemacht.

Im Folgenden fehlen absichtlich zahlreiche Details, über die ich nicht sicher bin, sowie eine viel größere Menge an Informationen, die mir einfach nicht bekannt sind. Verwenden Sie diese Informationen auf eigenes Risiko.


GC-Konzepte

Der .NET-GC tritt zu inkonsistenten Zeiten auf, weshalb er als "nicht deterministisch" bezeichnet wird. Dies bedeutet, dass Sie sich nicht darauf verlassen können, dass er zu bestimmten Zeiten auftritt. Es ist auch ein Garbage Collector für Generationen, dh, es unterteilt Ihre Objekte in die Anzahl der GC-Durchläufe, die sie durchlaufen haben.

Objekte im Gen 0-Heap haben 0 Sammlungen durchlaufen. Diese wurden neu erstellt. Daher ist seit ihrer Instanziierung in letzter Zeit keine Sammlung mehr aufgetreten. Objekte in Ihrem Gen-1-Haufen haben einen Sammelpass durchlaufen, und Objekte in Ihrem Gen-2-Haufen haben ebenfalls 2 Sammelpass durchlaufen.

Es lohnt sich jetzt zu erwähnen, warum diese spezifischen Generationen und Partitionen entsprechend qualifiziert werden. Der .NET-GC erkennt nur diese drei Generationen, da die Auflistungsübergänge, die über diese drei Heaps gehen, alle geringfügig unterschiedlich sind. Einige Objekte überleben die Sammlung möglicherweise tausende Male. Der GC belässt diese lediglich auf der anderen Seite der Gen 2-Heap-Partition. Es macht keinen Sinn, sie irgendwo weiter zu partitionieren, da sie tatsächlich Gen 44 sind. Die Weitergabe der Sammlung ist die gleiche wie in Gen 2 Heap.

Diese spezifischen Generationen verfolgen semantische Zwecke sowie implementierte Mechanismen, die diese berücksichtigen, und auf diese werde ich gleich eingehen.


Was ist in einer Sammlung

Das Grundkonzept eines GC-Sammlungsdurchlaufs besteht darin, dass jedes Objekt in einem Heap-Bereich überprüft wird, um festzustellen, ob noch Live-Verweise (GC-Wurzeln) auf diese Objekte vorhanden sind. Wenn ein GC-Stammverzeichnis für ein Objekt gefunden wird, bedeutet dies, dass aktuell ausgeführter Code dieses Objekt weiterhin erreichen und verwenden kann, sodass es nicht gelöscht werden kann. Wenn jedoch kein GC-Stammverzeichnis für ein Objekt gefunden wird, benötigt der ausgeführte Prozess das Objekt nicht mehr, sodass es entfernt werden kann, um Speicher für neue Objekte freizugeben.

Jetzt, nachdem ein Haufen Gegenstände aufgeräumt und einige in Ruhe gelassen wurden, gibt es einen unglücklichen Nebeneffekt: Freiraumlücken zwischen lebenden Objekten, in denen die toten entfernt wurden. Wenn diese Speicherfragmentierung in Ruhe gelassen wird, wird einfach Speicher verschwendet. In der Regel werden in Sammlungen die so genannten "Komprimierungsvorgänge" ausgeführt, bei denen alle verbleibenden Live-Objekte im Heap zusammengepresst werden, sodass der freie Speicher auf einer Seite des Heaps für Gen zusammenhängend ist 0.

Nachdem wir nun die Idee von drei Haufen Speicher haben, die alle nach der Anzahl der durchlaufenen Sammlungsdurchläufe unterteilt sind, wollen wir uns über die Gründe für diese Partitionen unterhalten.


Sammlung Gen 0

Da Gen 0 das absolut neueste Objekt ist, ist es in der Regel sehr klein, sodass Sie es sicher und häufig sammeln können . Die Häufigkeit stellt sicher, dass der Heap klein bleibt und die Sammlungen sehr schnell sind, da sie sich über einem so kleinen Heap sammeln. Dies basiert mehr oder weniger auf einer Heuristik, die besagt: Ein Großteil der von Ihnen erstellten temporären Objekte ist sehr temporär, so dass sie fast unmittelbar nach der Verwendung nicht mehr verwendet oder referenziert werden und somit gesammelt werden können.


Sammlung Gen 1

Da es sich bei Gen 1 um Objekte handelt, die nicht in diese sehr vorübergehende Kategorie von Objekten fallen, ist die Lebensdauer möglicherweise noch relativ kurz, da ein großer Teil der erstellten Objekte noch nicht lange verwendet wird. Aus diesem Grund sammelt Gen 1 auch ziemlich häufig und hält seinen Haufen wieder klein, damit seine Sammlungen schnell sind. Es wird jedoch davon ausgegangen, dass weniger Objekte temporär sind als Gen 0, sodass sie seltener als Gen 0 erfasst werden

Ich werde sagen, dass ich die technischen Mechanismen, die sich zwischen dem Sammelpass von Gen 0 und dem von Gen 1 unterscheiden, offen gesagt nicht kenne, wenn es überhaupt andere gibt als die Häufigkeit, mit der sie sammeln.


Gen 2 Sammlung

Gen 2 muss jetzt die Mutter aller Haufen sein, oder? Nun ja, das ist mehr oder weniger richtig. Hier leben alle Ihre permanenten Objekte - zum Beispiel das Objekt, in dem Sie Main()leben, und alles, was darauf Main()verweist, denn diese werden verwurzelt, bis Sie Main()am Ende Ihres Prozesses zurückkehren.

Da Gen 2 im Grunde genommen ein Eimer für alles ist, was die anderen Generationen nicht sammeln konnten, sind seine Objekte größtenteils dauerhaft oder zumindest langlebig. Wenn Sie also nur sehr wenig von dem, was in Gen 2 enthalten ist, erkennen, können Sie es tatsächlich sammeln, da es nicht häufig gesammelt werden muss. Dadurch kann die Erfassung auch langsamer erfolgen, da sie viel seltener ausgeführt wird. Im Grunde haben sie hier alle zusätzlichen Verhaltensweisen für ungerade Szenarien in Angriff genommen, weil sie die Zeit haben, sie auszuführen.


Großer Objekthaufen

Ein Beispiel für das zusätzliche Verhalten von Gen 2 ist, dass es auch die Auflistung auf dem Large Object Heap ausführt. Bisher habe ich mich ausschließlich mit dem Small Object Heap befasst, aber die .NET-Laufzeit ordnet aufgrund der oben genannten Komprimierung Dinge mit bestimmten Größen einem separaten Heap zu. Für die Komprimierung müssen Objekte verschoben werden, wenn die Auflistung auf dem Small Object Heap abgeschlossen ist. Wenn es in Gen 1 ein lebendes 10-MB-Objekt gibt, wird es viel länger dauern, bis die Komprimierung nach der Sammlung abgeschlossen ist, wodurch die Sammlung von Gen 1 verlangsamt wird. Damit wird dieses 10-MB-Objekt dem Large Object Heap zugewiesen und während der Gen 2 gesammelt, die so selten ausgeführt wird.


Finalisierung

Ein weiteres Beispiel sind Objekte mit Finalisierern. Sie platzieren einen Finalizer für ein Objekt, das auf Ressourcen außerhalb des Geltungsbereichs von .NETs GC verweist (nicht verwaltete Ressourcen). Der Finalizer ist die einzige Möglichkeit für den GC, die Erfassung einer nicht verwalteten Ressource anzufordern. Sie implementieren Ihren Finalizer, um die manuelle Erfassung / Entfernung / Freigabe der nicht verwalteten Ressource durchzuführen und sicherzustellen, dass sie nicht aus Ihrem Prozess ausläuft. Wenn der GC den Finalizer für Ihre Objekte ausführt, löscht Ihre Implementierung die nicht verwaltete Ressource, sodass der GC in der Lage ist, Ihr Objekt zu entfernen, ohne ein Ressourcenleck zu riskieren.

Der Mechanismus, mit dem Finalizer dies tun, besteht darin, direkt in einer Finalisierungswarteschlange referenziert zu werden. Wenn die Laufzeit ein Objekt mit einem Finalizer zuweist, fügt sie der Finalisierungswarteschlange einen Zeiger auf dieses Objekt hinzu und sperrt das Objekt (als Fixieren bezeichnet), sodass es durch die Komprimierung nicht verschoben wird, wodurch die Finalisierungswarteschlangenreferenz unterbrochen wird. Wenn Sammlungsdurchläufe stattfinden, hat Ihr Objekt möglicherweise kein GC-Stammverzeichnis mehr, aber die Finalisierung muss ausgeführt werden, bevor es gesammelt werden kann. Wenn das Objekt also tot ist, verschiebt die Sammlung seine Referenz aus der Finalisierungswarteschlange und platziert eine Referenz darauf in der sogenannten "FReachable" -Warteschlange. Dann geht die Sammlung weiter. Zu einem anderen "nicht deterministischen" Zeitpunkt in der Zukunft, Ein separater Thread, der als Finalizer-Thread bezeichnet wird, durchläuft die FReachable-Warteschlange und führt die Finalizer für jedes der referenzierten Objekte aus. Nachdem es fertig ist, ist die FReachable-Warteschlange leer, und es wurde ein bisschen in die Kopfzeile jedes Objekts gekippt, das angibt, dass keine Finalisierung erforderlich ist. (Dieses Bit kann auch manuell mit gekippt werden.)GC.SuppressFinalizedie gemeinsam ist Dispose()Methoden), habe ich auch den Verdacht hat es die Objekte steckte los, aber zitieren Sie mich nicht daran. Die nächste Sammlung, die sich auf dem Haufen befindet, in dem sich dieses Objekt befindet, wird es endlich sammeln. Die Sammlungen von Gen 0 berücksichtigen Objekte mit dem für die Finalisierung erforderlichen Bit nicht einmal, sondern befördern sie automatisch, ohne auch nur nach ihrer Wurzel zu suchen. Ein unbewurzeltes Objekt, das in Gen 1 FReachablefinalisiert werden muss , wird in die Warteschlange geworfen , aber die Sammlung macht nichts anderes damit, sodass es in Gen 2 lebt. Auf diese Weise werden alle Objekte, die einen Finalizer haben, und nicht GC.SuppressFinalizewird in Gen 2 gesammelt.

Jimmy Hoffa
quelle
4
@FlorianMargaine ja ... etwas über "GC" in allen Implementierungen zu sagen macht wirklich keinen Sinn ..
Jimmy Hoffa
10
tl; dr: Verwenden Sie stattdessen Objektpools.
Robert Harvey
5
tl; dr: Für das Timing / Profiling kann es nützlich sein.
Kutschkem
3
@Den nach dem Lesen meiner Beschreibung der Mechanik (wie ich sie verstehe), was wäre der Vorteil, wie Sie es sehen? Sie säubern eine große Anzahl von Gegenständen - im SOH (oder LOH?)? Haben Sie gerade andere Threads veranlasst, für diese Sammlung anzuhalten? Hat diese Sammlung nur doppelt so viele Objekte zu Gen 2 befördert, als sie ausgelöscht hat? Hat die Sammlung bei LOH eine Verdichtung verursacht (haben Sie sie aktiviert?)? Wie viele GC-Heaps haben Sie und befindet sich Ihr GC im Server- oder Desktop-Modus? GC ist ein verdammter Eisberg, der Verrat ist unter Wasser. Lenke einfach klar. Ich bin nicht schlau genug, um bequem zu sammeln.
Jimmy Hoffa
4
@RobertHarvey Objektpools sind auch keine Wunderwaffe. Die Generation 0 des Garbage Collectors ist praktisch bereits ein Objektpool. Sie ist normalerweise so bemessen, dass sie in die kleinste Cache-Ebene passt. Daher werden neue Objekte im Allgemeinen in einem Speicher erstellt, der sich bereits im Cache befindet. Ihr Objektpool konkurriert jetzt mit dem GC-Kindergarten um den Cache. Wenn die Summe aus dem GC-Kindergarten und Ihrem Pool größer als der Cache ist, werden Sie offensichtlich Cache-Ausfälle haben. Und wenn Sie jetzt Parallelität verwenden möchten, müssen Sie die Synchronisierung erneut implementieren und sich über falsche Freigabe Gedanken machen.
Doval
68

Leider geht dort niemand auf solche Fälle ein.

Ich werde einige Beispiele geben. Alles in allem ist es selten eine gute Idee, einen GC zu erzwingen, aber es kann sich absolut lohnen. Diese Antwort stammt aus meiner Erfahrung mit .NET- und GC-Literatur. Es sollte sich gut auf andere Plattformen übertragen lassen (zumindest auf solche mit einem signifikanten GC).

  • Benchmarks verschiedener Art. Sie möchten einen bekannten verwalteten Heap-Status, wenn ein Benchmark beginnt, damit der GC bei Benchmarks nicht nach dem Zufallsprinzip ausgelöst wird. Wenn Sie einen Benchmark wiederholen, möchten Sie, dass bei jeder Wiederholung die gleiche Anzahl und Menge an GC-Arbeiten durchgeführt wird.
  • Plötzliche Freigabe von Ressourcen. Zum Beispiel das Schließen eines wichtigen GUI-Fensters oder das Aktualisieren eines Caches (und damit das Freigeben des alten potenziell großen Cacheinhalts). Der GC kann dies nicht erkennen, da Sie lediglich einen Verweis auf null setzen. Die Tatsache, dass dies ein gesamtes Objektdiagramm verwaist, ist nicht leicht zu erkennen.
  • Freigabe nicht verwalteter Ressourcen, die durchgesickert sind . Dies sollte natürlich nie passieren, aber ich habe Fälle gesehen, in denen in einer Bibliothek eines Drittanbieters Daten verloren gegangen sind (z. B. COM-Objekte). Der Entwickler war gezwungen, manchmal eine Sammlung zu veranlassen.
  • Interaktive Anwendungen wie Spiele . Während des Spiels sind die Zeitbudgets pro Frame sehr hoch (60 Hz => 16 ms pro Frame). Um Probleme zu vermeiden, benötigen Sie eine Strategie für den Umgang mit GCs. Eine solche Strategie besteht darin, G2-GCs so weit wie möglich zu verzögern und zu einem geeigneten Zeitpunkt wie einem Ladebildschirm oder einer Zwischensequenz zu erzwingen. Der GC kann nicht wissen, wann der beste Moment dafür ist.
  • Latenzkontrolle im Allgemeinen. Einige Webanwendungen deaktivieren GCs und führen regelmäßig eine G2-Sammlung durch, während die Rotation des Lastenausgleichs deaktiviert ist. Auf diese Weise wird die G2-Latenz dem Benutzer nie angezeigt.

Wenn Ihr Ziel der Durchsatz ist, ist der GC umso besser, je seltener er ist. In diesen Fällen kann das Erzwingen einer Auflistung keine positiven Auswirkungen haben (mit Ausnahme von Problemen wie der Erhöhung der CPU-Cache-Auslastung durch Entfernen von toten Objekten, die sich in den aktiven Objekten befinden). Die Batch-Sammlung ist für alle mir bekannten Sammler effizienter. Für Produktions-App im Steady-State-Memory-Verbrauch hilft das Induzieren eines GC nicht.

Die oben angegebenen Beispiele zielen auf Konsistenz und Beschränkung der Speichernutzung ab. In diesen Fällen können induzierte GCs sinnvoll sein.

Es scheint eine weit verbreitete Idee zu sein, dass der GC eine göttliche Einheit ist, die eine Sammlung auslöst, wann immer dies in der Tat optimal ist. Kein GC, von dem ich weiß, ist so ausgefeilt und es ist in der Tat sehr schwer, für den GC optimal zu sein. Der GC weiß weniger als der Entwickler. Die Heuristik basiert auf Speicherzählern und Dingen wie Erfassungsrate und so weiter. Die Heuristiken sind in der Regel gut, erfassen jedoch keine plötzlichen Änderungen im Anwendungsverhalten, z. B. die Freigabe großer Mengen an verwaltetem Speicher. Es ist auch blind für nicht verwaltete Ressourcen und für Latenzanforderungen.

Beachten Sie, dass die GC-Kosten von der Größe des Heapspeichers und der Anzahl der Referenzen auf dem Heapspeicher abhängen. Auf einem kleinen Haufen können die Kosten sehr gering sein. Ich habe G2-Erfassungsraten mit .NET 4.5 von 1-2 GB / s auf einer Produktions-App mit 1 GB Heap-Größe gesehen.

usr
quelle
Für den Fall der Latenzkontrolle, denke ich, können Sie dies stattdessen auch nach Bedarf tun (dh wenn die Speichernutzung einen bestimmten Schwellenwert überschreitet).
Paŭlo Ebermann
3
+1 für den vorletzten Absatz. Einige Leute haben die gleiche Einstellung zu Compilern und bezeichnen fast alles als "vorzeitige Optimierung". Normalerweise erzähle ich ihnen etwas Ähnliches.
Honza Brabec
2
+1 auch für diesen Absatz. Ich finde es schockierend, dass die Leute denken, ein von jemand anderem geschriebenes Computerprogramm müsse die Leistungsmerkmale ihres Programms unbedingt besser verstehen als sie selbst.
Mehrdad
1
@HonzaBrabec Das Problem ist in beiden Fällen dasselbe: Wenn Sie denken, Sie wissen es besser als der GC oder der Compiler, dann ist es sehr leicht, sich selbst zu verletzen. Wenn Sie tatsächlich mehr wissen, optimieren Sie nur, wenn Sie wissen, dass dies nicht verfrüht ist.
SVICK
27

Im Allgemeinen wird ein Garbage Collector bei "Speicherdruck" gesammelt, und es wird als eine gute Idee angesehen, ihn nicht zu anderen Zeiten sammeln zu lassen, da dies zu Leistungsproblemen oder sogar zu merklichen Unterbrechungen bei der Ausführung Ihres Programms führen kann. Tatsächlich ist der erste Punkt vom zweiten abhängig: Für einen Müllsammler der Generation läuft er umso effizienter, je höher das Verhältnis von Müll zu guten Objekten ist, um so weniger Zeit wird für die Unterbrechung des Programms aufgewendet , es muss zögern und den Müll so viel wie möglich stapeln lassen.

Die geeignete Zeit zum manuellen Aufrufen des Garbage Collectors ist dann, wenn Sie etwas erledigt haben, das 1) wahrscheinlich viel Müll verursacht hat und 2) der Benutzer einige Zeit in Anspruch nimmt und das System nicht mehr reagiert sowieso. Ein klassisches Beispiel ist das Ende des Ladens eines großen Objekts (eines Dokuments, eines Modells, einer neuen Ebene usw.).

Mason Wheeler
quelle
12

Eine Sache, die niemand erwähnt hat, ist, dass der Windows-GC zwar erstaunlich gut ist, der GC auf der Xbox jedoch Müll ist (Wortspiel beabsichtigt) .

Wenn Sie also ein XNA-Spiel programmieren, das auf XBox ausgeführt werden soll, ist es unbedingt erforderlich, die Speicherbereinigung auf die richtigen Momente abzustimmen, da sonst schreckliche, zeitweise auftretende FPS-Probleme auftreten können. Darüber hinaus ist es bei XBox üblich, structs viel häufiger als sonst zu verwenden, um die Anzahl der Objekte zu minimieren, die mit Müll gesammelt werden müssen.

BlueRaja - Danny Pflughoeft
quelle
4

Die Garbage Collection ist in erster Linie ein Tool zur Speicherverwaltung. Daher sammeln sich Müllsammler, wenn Speicherdruck besteht.

Moderne Abfallsammler sind sehr gut und werden immer besser. Daher ist es unwahrscheinlich, dass Sie sie durch manuelles Sammeln verbessern können. Selbst wenn Sie die Dinge heute noch verbessern können, kann es durchaus sein, dass eine zukünftige Verbesserung des ausgewählten Garbage Collectors Ihre Optimierung ineffektiv oder sogar kontraproduktiv macht.

Jedoch , Müllsammler versuchen normalerweise keine Verwendung von Ressourcen zu optimieren andere als Speicher. In Umgebungen mit Speicherbereinigung verfügen die meisten wertvollen Nicht-Speicherressourcen über eine closeMethode oder eine ähnliche Methode. In einigen Fällen ist dies jedoch aus irgendeinem Grund nicht der Fall, z. B. bei der Kompatibilität mit einer vorhandenen API.

In diesen Fällen kann es sinnvoll sein, die Garbage Collection manuell aufzurufen, wenn Sie wissen, dass eine wertvolle Nicht-Speicherressource verwendet wird.

RMI

Ein konkretes Beispiel hierfür ist Javas Remote Method Invocation. RMI ist eine Remote Procedure Call Library. In der Regel verfügen Sie über einen Server, der verschiedene Objekte für die Verwendung durch Clients zur Verfügung stellt. Wenn ein Server weiß, dass ein Objekt nicht von Clients verwendet wird, ist dieses Objekt für die Garbage Collection berechtigt.

Der Server weiß dies jedoch nur, wenn der Client es ihm mitteilt, und der Client teilt dem Server nur mit, dass er kein Objekt mehr benötigt, sobald der Client den Müll eingesammelt hat, der es verwendet.

Dies stellt ein Problem dar, da der Client möglicherweise über viel freien Speicher verfügt und daher die Garbage Collection möglicherweise nicht häufig ausgeführt wird. In der Zwischenzeit hat der Server möglicherweise viele nicht verwendete Objekte im Speicher, die er nicht erfassen kann, da er nicht weiß, dass der Client sie nicht verwendet.

Die Lösung in RMI besteht darin, dass der Client die Garbage Collection regelmäßig ausführt, auch wenn viel Speicher frei ist, um sicherzustellen, dass Objekte sofort auf dem Server erfasst werden.

James_pic
quelle
"In diesen Fällen kann es sinnvoll sein, die Garbage Collection manuell aufzurufen, wenn Sie wissen, dass eine wertvolle Nicht-Speicherressource verwendet wird." - Wenn eine Nicht-Speicherressource verwendet wird, sollten Sie einen usingBlock verwenden oder eine CloseMethode anderweitig aufrufen Stellen Sie sicher, dass die Ressource so schnell wie möglich gelöscht wird. Sich beim Bereinigen von Nicht-Speicherressourcen auf GC zu verlassen, ist unzuverlässig und verursacht alle Arten von Problemen (insbesondere bei Dateien, die für den Zugriff gesperrt werden müssen und daher nur einmal geöffnet werden können).
Jules
Und wie in der Antwort angegeben, closeist usingdies der richtige Ansatz , wenn eine Methode verfügbar ist (oder die Ressource mit einem Block verwendet werden kann). Die Antwort befasst sich speziell mit den seltenen Fällen, in denen diese Mechanismen nicht verfügbar sind.
James_pic
Meine persönliche Meinung ist, dass jede Schnittstelle, die eine Nicht-Speicherressource verwaltet, aber keine Abschlussmethode bereitstellt, eine Schnittstelle ist, die nicht verwendet werden sollte , da es keine Möglichkeit gibt, sie zuverlässig zu verwenden.
Jules
@Jules Ich stimme zu, aber manchmal ist es unvermeidlich. Manchmal lecken Abstraktionen und es ist besser, eine undichte Abstraktion zu verwenden, als keine Abstraktion zu verwenden. Manchmal müssen Sie mit altem Code arbeiten, der Sie dazu auffordert, Versprechen abzugeben, von denen Sie wissen, dass Sie diese nicht einhalten können. Ja, es ist selten und sollte nach Möglichkeit vermieden werden, und es gibt einen Grund, warum all diese Warnungen dazu führen, dass die Müllabfuhr erzwungen wird, aber diese Situationen tauchen auf, und das OP fragte, wie diese Situationen aussehen - worauf ich geantwortet habe .
James_pic
2

In den meisten Fällen wird empfohlen, keine Garbage Collection zu erzwingen. (Jedes System, an dem ich gearbeitet habe, hatte erzwungene Speicherbereinigungen, unterstrich Probleme, die, wenn sie gelöst worden wären, die Notwendigkeit beseitigt hätten, die Speicherbereinigung zu erzwingen, und beschleunigte das System erheblich.)

Es gibt ein paar Fälle , wenn Sie mehr über die Speichernutzung wissen dann der Garbage Collector tut. Es ist unwahrscheinlich, dass dies in einer Mehrbenutzeranwendung oder einem Dienst, der auf mehrere Anforderungen gleichzeitig reagiert, der Fall ist.

Bei einigen Batch-Verarbeitungen kennen Sie jedoch mehr als den GC. ZB betrachten Sie eine Anwendung, die.

  • Erhält eine Liste mit Dateinamen in der Befehlszeile
  • Verarbeitet eine einzelne Datei und schreibt das Ergebnis in eine Ergebnisdatei.
  • Erstellt während der Verarbeitung der Datei viele verknüpfte Objekte, die erst gesammelt werden können, wenn die Verarbeitung der Datei abgeschlossen ist (z. B. ein Analysebaum).
  • Behält den Übereinstimmungsstatus zwischen den verarbeiteten Dateien nicht bei .

Möglicherweise können Sie (nach sorgfältigen) Tests feststellen, dass Sie eine vollständige Garbage Collection erzwingen sollten, nachdem Sie die einzelnen Dateien verarbeitet haben.

Ein weiterer Fall ist ein Dienst, der alle paar Minuten zur Verarbeitung einiger Elemente aufwacht und im Schlaf keinen Status beibehält . Dann kann es sich lohnen, kurz vor dem Schlafengehen eine vollständige Sammlung zu erzwingen .

Ich würde nur in Betracht ziehen, eine Sammlung zu erzwingen, wenn ich weiß, dass in letzter Zeit viele Objekte erstellt wurden und derzeit nur sehr wenige Objekte referenziert werden.

Ich hätte lieber eine Garbage Collection-API, wenn ich Hinweise auf diese Art von Dingen geben könnte, ohne dass ich einen GC erzwingen müsste.

Siehe auch " Rico Marianis Performance-Leckerbissen "

Ian
quelle
2

Es gibt mehrere Fälle, in denen Sie gc () selbst aufrufen möchten.

  • [ Einige Leute sagen, dass dies nicht gut ist, weil es Objekte in den Raum älterer Generationen befördern kann, was meiner Meinung nach keine gute Sache ist. Es ist jedoch NICHT immer wahr, dass es immer Objekte geben wird, die beworben werden können. Es ist sicherlich möglich, dass nach diesem gc()Aufruf nur noch sehr wenige Objekte in den Raum älterer Generationen verschoben werden. ] Wenn Sie eine große Sammlung von Objekten erstellen und viel Speicher verwenden möchten. Sie möchten einfach so viel Platz wie möglich für die Vorbereitung freigeben. Das ist nur gesunder Menschenverstand. Wenn Sie gc()manuell aufrufen , wird ein Teil dieser großen Sammlung von Objekten, die Sie in den Speicher laden, nicht auf redundante Referenzgraphen überprüft. Kurz gesagt, wenn Sie ausführen, gc()bevor Sie viel in den Speicher laden, wird diegc() Der während des Ladevorgangs verursachte Druck verringert sich um mindestens ein Mal, wenn das Laden beginnt, Speicherdruck zu erzeugen.
  • Wenn Sie eine große Sammlung von geladen habengroßObjekte und Sie werden wahrscheinlich nicht mehr Objekte in den Speicher laden. Kurz gesagt, Sie wechseln von der Erstellungsphase zur Verwendungsphase. Durch den Aufruf gc()je nach Ausführung, wird der Speicher in verwendet verdichtet die Cache - Ort massiv verbessert. Dies führt zu einer massiven Leistungsverbesserung, die Sie durch die Profilerstellung nicht erzielen .
  • Ähnlich wie bei der ersten, aber aus der Sicht, dass Sie, wenn Sie dies tun gc()und die Speicherverwaltungsimplementierung dies unterstützt, eine viel bessere Kontinuität für Ihren physischen Speicher schaffen. Dies macht die neue große Sammlung von Objekten wieder durchgängiger und kompakter, was wiederum die Leistung verbessert
InformedA
quelle
1
Kann jemand auf den Grund der Ablehnung hinweisen? Ich weiß selbst nicht genug, um die Antwort zu beurteilen (auf den ersten Blick macht es für mich irgendwie Sinn).
Omega
1
Ich vermute, Sie haben eine Ablehnung für den dritten Punkt. Möglicherweise auch für die Aussage "Das ist nur gesunder Menschenverstand".
immibis
2
Wenn Sie eine große Sammlung von Objekten erstellen, sollte der GC intelligent genug sein, um zu wissen, ob eine Sammlung benötigt wird. Dasselbe gilt, wenn der Speicher komprimiert werden muss. Sich auf den GC zu verlassen, um die Speicherlokalität verwandter Objekte zu optimieren, scheint nicht realisierbar. Ich denke, Sie können andere Lösungen finden (struct, unsicher, ...). (Ich bin nicht der Downvoter).
Guillaume
3
Ihre erste Vorstellung von einer guten Zeit ist meiner Meinung nach nur ein schlechter Rat. Die Chancen sind hoch , dass es eine Sammlung vor kurzem war so Ihr Versuch zu sammeln wieder einfach wird willkürlich Objekte spätere Generationen zu fördern, die fast immer schlecht ist. Spätere Generationen haben Sammlungen, die von Anfang an länger dauern, und eine Vergrößerung der Heap-Größe, um "so viel Platz wie möglich freizugeben", macht dies nur problematischer. Wenn Sie im Begriff sind, den Speicherdruck mit einer Last zu erhöhen, werden Sie wahrscheinlich trotzdem mit dem Induzieren von Sammlungen beginnen, die langsamer ablaufen, da Gen1 / 2
Jimmy Hoffa am
2
By calling gc() depending on implementation, the memory in used will be compacted which massively improves cache locality. This will result in massive improve in performance that you will not get from profiling.Wenn Sie eine Tonne von Objekten in einer Reihe zuordnen, sind die Quoten bereits komprimiert. Wenn überhaupt, kann die Müllabfuhr sie leicht durcheinander bringen. In beiden Fällen hat die Verwendung von Datenstrukturen, die dicht sind und nicht zufällig im Speicher herumspringen, eine größere Auswirkung. Wenn Sie eine naive verknüpfte Liste mit einem Element pro Knoten verwenden, wird dies nicht durch manuelle GC-Tricks wettgemacht.
Doval
2

Ein reales Beispiel:

Ich hatte eine Webanwendung, die von einer sehr großen Datenmenge Gebrauch machte, die sich selten änderte und auf die sehr schnell zugegriffen werden musste (schnell genug für eine Antwort per Tastendruck über AJAX).

Hier ist es naheliegend, den entsprechenden Graphen in den Speicher zu laden und von dort aus auf ihn zuzugreifen, anstatt auf die Datenbank zuzugreifen, und den Graphen zu aktualisieren, wenn sich die Datenbank ändert.

Da die Datenmenge jedoch sehr groß ist, hätte eine nicht vorhandene Last mindestens 6 GB Speicherplatz in Anspruch genommen, da sie in Zukunft zunehmen wird. (Ich habe keine genauen Zahlen, als klar war, dass mein 2-GB-Rechner mit mindestens 6 GB zu kämpfen hatte, hatte ich alle Messungen, die ich brauchte, um zu wissen, dass es nicht funktionieren würde.)

Glücklicherweise gab es in diesem Datensatz eine große Anzahl von Eis am Stiel-unveränderlichen Objekten, die einander gleich waren. Sobald ich herausgefunden hatte, dass eine bestimmte Charge mit einer anderen Charge identisch ist, konnte ich einen Verweis auf die andere aliasen, sodass viele Daten erfasst und somit alles in weniger als einen halben Gig gepasst werden konnte.

Alles schön und gut, aber dafür immer noch über 6 GB Objekte innerhalb einer halben Minute durchwühlen, um in diesen Zustand zu gelangen. GC war für sich allein gelassen und kam nicht zurecht. Die Aktivitätsspitze gegenüber dem üblichen Muster der Anwendung (viel weniger stark bei Aufhebungen pro Sekunde) war zu scharf.

Das periodische Aufrufen GC.Collect()während dieses Erstellungsprozesses bedeutete, dass das Ganze reibungslos funktionierte. Natürlich habe ich GC.Collect()den Rest der Zeit, in der die Anwendung ausgeführt wird , nicht manuell aufgerufen .

Dieser reale Fall ist ein gutes Beispiel für die Richtlinien, wann wir verwenden sollten GC.Collect():

  1. Verwendung mit einem relativ seltenen Fall, in dem viele Objekte zur Sammlung bereitgestellt wurden (es wurden Megabyte zur Verfügung gestellt, und dieses Erstellen von Grafiken war während der gesamten Lebensdauer der Anwendung ein sehr seltener Fall (etwa eine Minute pro Woche).
  2. Tun Sie dies, wenn ein Leistungsverlust relativ erträglich ist. Dies geschah nur beim Start der Anwendung. (Ein weiteres gutes Beispiel für diese Regel sind Levels während eines Spiels oder andere Punkte in einem Spiel, in denen die Spieler nicht durch eine kurze Pause verärgert werden.)
  3. Profil, um sicher zu sein, gibt es wirklich eine Verbesserung. (Ziemlich einfach; "Es funktioniert" schlägt fast immer "Es funktioniert nicht").

Die meiste Zeit, wenn ich dachte, ich könnte einen Fall haben, in dem GC.Collect()es sich lohnt, anzurufen, weil Punkt 1 und 2 zutrafen, schlug Punkt 3 vor, dass die Dinge schlechter oder zumindest nicht besser wurden (und mit wenig oder keiner Verbesserung würde ich es tun Neigen Sie dazu, nicht über einen Anruf zu telefonieren, da sich dieser Ansatz über die Lebensdauer einer Anwendung mit größerer Wahrscheinlichkeit als besser erweist.

Jon Hanna
quelle
0

Ich habe eine Verwendung für die Müllentsorgung, die etwas unorthodox ist.

Es gibt diese fehlgeleitete Praxis, die leider in der C # -Welt sehr verbreitet ist, die Objektentsorgung unter Verwendung der hässlichen, klobigen, uneleganten und fehleranfälligen IDisposable-Disposing-Sprache zu implementieren . MSDN beschreibt es ausführlich , und viele Leute schwören darauf, befolgen es religiös, diskutieren stundenlang , wie es genau gemacht werden soll usw.

(Bitte beachten Sie, dass das, was ich hier als hässlich bezeichne, nicht das Objektentsorgungsmuster selbst ist. Was ich als hässlich bezeichne, ist die bestimmte IDisposable.Dispose( bool disposing )Redewendung.)

Diese Redewendung wurde erfunden, weil es angeblich unmöglich ist, zu garantieren, dass der Garbage Collector den Destruktor Ihrer Objekte immer aufruft, um Ressourcen zu bereinigen. Daher führen die Benutzer die Ressourcenbereinigung in diesem Bereich durch IDisposable.Dispose(), und falls sie dies vergessen, versuchen sie es auch noch einmal innerhalb des Destruktors. Weißt du, nur für den Fall.

Dann müssen IDisposable.Dispose()möglicherweise sowohl verwaltete als auch nicht verwaltete Objekte bereinigt werden. Die verwalteten Objekte können jedoch nicht bereinigt werden, wenn sie IDisposable.Dispose()im Destruktor aufgerufen werden, da sie zu diesem Zeitpunkt bereits vom Garbage Collector bearbeitet wurden Ist dies eine Notwendigkeit für eine separate Dispose()Methode, die ein bool disposingFlag akzeptiert, um zu wissen, ob sowohl verwaltete als auch nicht verwaltete Objekte bereinigt werden sollen, oder nur nicht verwaltete.

Entschuldigung, aber das ist einfach verrückt.

Ich gehe nach Einsteins Axiom vor, das besagt, dass die Dinge so einfach wie möglich sein sollten, aber nicht einfacher. Natürlich können wir die Bereinigung von Ressourcen nicht auslassen, daher muss die einfachste mögliche Lösung zumindest diese beinhalten. Die nächst einfachere Lösung besteht darin, immer alles zu dem Zeitpunkt zu entsorgen, an dem es entsorgt werden soll, ohne die Dinge zu komplizieren, indem der Destruktor als alternative Alternative herangezogen wird.

Streng genommen ist es natürlich unmöglich zu garantieren, dass kein Programmierer jemals den Fehler begeht, das Aufrufen zu vergessen IDisposable.Dispose(), aber wir können den Destruktor verwenden, um diesen Fehler aufzufangen. Eigentlich ist es sehr einfach: Der Destruktor muss nur einen Protokolleintrag generieren, wenn er feststellt, dass das disposedFlag des Einwegobjekts nie gesetzt wurde true. Die Verwendung des Destruktors ist daher kein wesentlicher Bestandteil unserer Entsorgungsstrategie, sondern unser Mechanismus zur Qualitätssicherung. Und da dies nur ein Test im Debug-Modus ist, können wir unseren gesamten Destruktor in einem #if DEBUGBlock platzieren, sodass wir in einer Produktionsumgebung niemals eine Zerstörungsstrafe erleiden. (Die IDisposable.Dispose( bool disposing )Redewendung schreibt das vorGC.SuppressFinalize() sollte genau aufgerufen werden, um den Overhead der Finalisierung zu verringern, aber mit meinem Mechanismus ist es möglich, diesen Overhead für die Produktionsumgebung vollständig zu vermeiden.)

Worauf es hinausläuft, ist das ewige Argument zwischen hartem und weichem Fehler : Das IDisposable.Dispose( bool disposing )Idiom ist ein Ansatz mit weichem Fehler und stellt einen Versuch dar, dem Programmierer zu ermöglichen, den Aufruf nach Möglichkeit zu vergessen, Dispose()ohne dass das System ausfällt. Der Hard-Error-Ansatz besagt, dass der Programmierer immer sicherstellen muss, dass dieser Dispose()aufgerufen wird. Die Strafe, die in den meisten Fällen vom Hard Error-Ansatz vorgegeben wird, ist ein Assertionsfehler. In diesem speziellen Fall machen wir jedoch eine Ausnahme und verringern die Strafe für die einfache Ausgabe eines Fehlerprotokolleintrags.

Damit dieser Mechanismus funktioniert, muss die DEBUG-Version unserer Anwendung vor dem Beenden eine vollständige Müllentsorgung durchführen, um sicherzustellen, dass alle Destruktoren aufgerufen werden und somit alle IDisposableObjekte aufgefangen werden, die wir vergessen haben, zu entsorgen.

Mike Nakis
quelle
Now, strictly speaking, it is of course impossible to guarantee that no programmer will ever make the mistake of forgetting to invoke IDisposable.Dispose()Eigentlich nicht, obwohl ich nicht denke, dass C # dazu in der Lage ist. Stellen Sie die Ressource nicht zur Verfügung. Geben Sie stattdessen eine DSL an, um alles zu beschreiben, was Sie damit tun (im Grunde genommen eine Monade), sowie eine Funktion, die die Ressource erfasst, die Dinge erledigt, sie freigibt und das Ergebnis zurückgibt. Der Trick besteht darin, das Typsystem zu verwenden, um sicherzustellen, dass ein Verweis auf die Ressource, den jemand herausschmuggelt, nicht in einem anderen Aufruf der Ausführungsfunktion verwendet werden kann.
Doval
2
Das Problem mit Dispose(bool disposing)(das nicht definiert IDisposableist, besteht darin, dass es zum Bereinigen von verwalteten und nicht verwalteten Objekten verwendet wird, die das Objekt als Feld hat (oder anderweitig dafür verantwortlich ist), wodurch das falsche Problem gelöst wird. Wenn Sie alles umbrechen Nicht verwaltete Objekte in einem verwalteten Objekt, für die keine anderen Einwegobjekte zu befürchten Dispose()sind, sind entweder alle Methoden eine von diesen (der Finalisierer führt bei Bedarf die gleiche Bereinigung durch) oder es müssen nur verwaltete Objekte entsorgt werden (er hat keinen Finalisierer) überhaupt), und die Notwendigkeit für bool disposingverschwindet
Jon Hanna
-1 Schlechter Rat, da die Finalisierung tatsächlich funktioniert. Ich stimme vollkommen mit Ihrem Standpunkt überein, dass die dispose(disposing)Redewendung Terribad ist, aber ich sage dies, weil die Leute diese Technik und Finalizer so oft anwenden, wenn sie nur verwaltete Ressourcen haben ( DbConnectionzum Beispiel wird das Objekt verwaltet , es wird nicht beschlagnahmt oder gemarshallt), und SIE SOLLTEN NUR IMPLEMENTIEREN SIE EINEN FINALISIERER MIT UNVERWALTETEM, PINVOKED, COM MARSHALLED ODER UNSICHEREM CODE . Ich detailliert oben in meiner Antwort , wie erschreckend teuer Finalizers sind, nicht nutzen sie , es sei denn , Sie haben nicht verwaltete in Ihrer Klasse Ressourcen.
Jimmy Hoffa
2
Ich möchte Ihnen beinahe +1 geben, nur weil Sie etwas entschlüsseln, das so viele Menschen als Kernbedeutung in der dispose(dispoing)Umgangssprache betrachten, aber die Wahrheit ist, dass dies nur deshalb so weit verbreitet ist, weil die Leute so viel Angst vor GC-Dingen haben, dass etwas so Unabhängiges wie Das ( disposesollte eigentlich mit GC zu tun haben) verdient es, dass sie nur das verschriebene Medikament einnehmen, ohne es überhaupt zu untersuchen. Gut, dass Sie es überprüft haben, aber Sie haben das größte Ganze verpasst (es ermutigt die Finalisten, häufiger als sie sein sollten)
Jimmy Hoffa
1
@ JimmyHoffa vielen Dank für Ihre Eingabe. Ich bin damit einverstanden, dass ein Finalizer normalerweise nur zum Freigeben nicht verwalteter Ressourcen verwendet werden sollte, aber sind Sie sich nicht einig, dass diese Regel beim DEBUG-Build nicht anwendbar ist und dass wir beim DEBUG-Build die Freiheit haben sollten, Finalizer zum Abfangen von Fehlern zu verwenden? Das ist alles, was ich hier vorschlage, also verstehe ich nicht, warum Sie damit Probleme haben. Siehe auch programmers.stackexchange.com/questions/288715/… für eine ausführlichere Erklärung dieses Ansatzes auf der Java-Seite der Welt.
Mike Nakis
0

Können Sie mir sagen, in welchem ​​Szenario es eigentlich eine gute oder vernünftige Idee ist, die Garbage Collection zu erzwingen? Ich frage nicht nach C # -Fällen, sondern nach allen Programmiersprachen mit Garbage Collector. Ich weiß, dass Sie GC nicht für alle Sprachen wie Java erzwingen können, aber nehmen wir an, Sie können es.

Das größte Szenario, das ich mir vorstellen kann, um die Garbage Collection zu erzwingen, ist eine unternehmenskritische Software, bei der logische Lecks gegenüber herabhängenden Zeigerabstürzen vorzuziehen sind, z. B. weil sie abstürzen in unerwarteten Zeiten könnte Menschenleben oder etwas in dieser Art kosten.

Wenn Sie sich einige der schäbigeren Indie-Spiele ansehen, die mit GC-Sprachen wie Flash-Spielen geschrieben wurden, dann lecken sie wie verrückt, aber sie stürzen nicht ab. Sie benötigen möglicherweise das Zehnfache des Speichers 20 Minuten, um das Spiel zu spielen, da ein Teil der Codebasis des Spiels vergessen hat, einen Verweis auf Null zu setzen oder ihn aus einer Liste zu entfernen, und die Bildraten möglicherweise zu leiden beginnen, aber das Spiel funktioniert immer noch. Ein ähnliches Spiel, das mit Shoddy-C- oder C ++ - Codierung geschrieben wurde, stürzt möglicherweise ab, wenn auf baumelnde Zeiger zugegriffen wird, die auf denselben Fehler bei der Ressourcenverwaltung zurückzuführen sind, aber es tritt nicht so häufig ein Fehler auf.

Für Spiele mag der Absturz in dem Sinne vorzuziehen sein, dass er schnell erkannt und behoben werden kann. Für ein unternehmenskritisches Programm kann ein Absturz zu völlig unerwarteten Zeiten jemanden töten. Ich denke, die Hauptfälle sind Szenarien, in denen kein Absturz oder andere Sicherheitsaspekte absolut kritisch sind, und ein logisches Leck ist im Vergleich eine relativ triviale Sache.

Das Hauptszenario, in dem es meiner Meinung nach schlecht ist, GC zu erzwingen, ist für Dinge gedacht, bei denen das logische Leck eigentlich weniger vorzuziehen ist als ein Absturz. Bei Spielen zum Beispiel wird der Absturz nicht unbedingt jemanden töten, und es kann sein, dass er während interner Tests leicht abgefangen und behoben werden kann, während ein logisches Leck selbst nach Auslieferung des Produkts unbemerkt bleibt, es sei denn, es ist so schwerwiegend, dass das Spiel innerhalb von Minuten nicht mehr spielbar ist . In einigen Bereichen ist ein leicht reproduzierbarer Absturz, der beim Testen auftritt, manchmal einem Leck vorzuziehen, das niemand sofort bemerkt.

Ein anderer Fall, bei dem ich mir vorstellen kann, dass es vorzuziehen ist, einem Team die GC aufzuzwingen, ist ein sehr kurzlebiges Programm, wie z. B. etwas, das über die Befehlszeile ausgeführt wird und eine Aufgabe ausführt und dann herunterfährt. In diesem Fall ist die Lebensdauer des Programms zu kurz, um ein logisches Leck nicht mehr zu belasten. Logische Lecks werden selbst bei großen Ressourcen in der Regel erst Stunden oder Minuten nach dem Ausführen der Software problematisch. Daher ist es unwahrscheinlich, dass eine Software, die nur für 3 Sekunden ausgeführt werden soll, jemals Probleme mit logischen Lecks aufweist, und dies kann zu erheblichen Problemen führen Es ist einfacher, solche kurzlebigen Programme zu schreiben, wenn das Team nur GC verwendet.


quelle