14 Stimmen

Wann sollte man die Garbage Collection NICHT verwenden?

Die offensichtlichen Gründe für den Verzicht auf Garbage Collection sind harte Echtzeit, stark begrenzter Speicher und der Wunsch, mit Zeigern zu hantieren. Gibt es noch andere, weniger diskutierte, gute Gründe, warum jemand die manuelle Speicherverwaltung der GC vorziehen würde?

1voto

J D Punkte 47190

Gibt es noch andere, weniger diskutierte, gute Gründe, warum jemand die manuelle Speicherverwaltung der GC vorziehen würde?

Das vielleicht wichtigste unausgesprochene Thema ist der Code, den die VM einfügt, damit sie mit der GC harmoniert. Insbesondere verursachen alle GCs in Produktionsqualität stillschweigend eine Schreibsperre, wenn ein Zeiger in den Heap geschrieben wird.

Das folgende F#-Programm erstellt z. B. ein Array mit 10.000 Ints und tauscht sie dann aus:

do
  let xs = Array.init 10000 int
  let timer = System.Diagnostics.Stopwatch.StartNew()
  for i=0 to xs.Length-2 do
    for j=i+1 to xs.Length-1 do
      let t = xs.[i]
      xs.[i] <- xs.[j]
      xs.[j] <- t
  printfn "%f" timer.Elapsed.TotalSeconds

Ändern Sie das int a string und das Programm läuft 2x langsamer, weil ints direkt ausgetauscht werden können, während der Austausch von Referenztypen zwei Schreibsperren nach sich ziehen muss.

Eine weitere wichtige Situation, die gerne unter den Teppich gekehrt wird, ist das pathologische Verhalten der konventionellen GCs. Die meisten GCs arbeiten heute nach dem Generationsprinzip, d.h. sie teilen die Kinder in ein Kinderzimmer ein und evakuieren dann die Überlebenden in eine ältere Generation (typischerweise mark-sweep). Dies funktioniert gut, wenn die Generationshypothese (dass Objekte jung sterben und alte Objekte selten auf neuere Objekte verweisen) zutrifft, weil die meisten Objekte in der Nursery tot sind, wenn sie effizient gesweept wird. Aber Objekte sterben nicht immer jung, und alte Objekte sind manchmal voll von Verweisen auf neue Objekte.

Das pathologische Verhalten einer generationalen GC zeigt sich insbesondere dann, wenn ein Programm eine große, auf Arrays basierende, veränderbare Datenstruktur (z. B. Hash-Tabelle, Hash-Set, Stack, Queue oder Heap) alloziert und diese dann mit frisch allozierten Objekten füllt. Diese neuen Objekte überleben, weil sie von einem älteren Objekt referenziert werden, was die Generationshypothese vollständig verletzt. Folglich sind Lösungen, die GCs verwenden, typischerweise 3x langsamer als hier notwendig.

FWIW, ich glaube, dass mark-region GCs das Potenzial haben, diese Probleme in Zukunft zu umgehen. In einer Mark-Region GC ist die alte Generation eine Sammlung ehemaliger Nurses. Wenn sich eine thread-lokale Region füllt und sich herausstellt, dass sie die meisten erreichbaren Objekte enthält, kann die gesamte Region logischerweise in die alte Generation migriert werden, ohne dass irgendwelche Objekte kopiert werden, und es kann eine neue Nursery zugewiesen werden (oder eine nicht volle alte Nursery kann recycelt werden).

0 Stimmen

Eine Sache, von der ich dachte, dass sie in einem eingebetteten GC-Framework nützlich wäre, wäre eine Möglichkeit zu spezifizieren, dass ein bestimmter Typ keine verschachtelten veränderbaren starken Referenzen enthalten kann (und dass das Framework dies durchsetzt); wenn man eine Liste aller Objekte der älteren Generation, die verschachtelte GC-Referenzen enthalten könnten, führen würde und diese Liste bei jedem gen0-Scan scannen würde, könnte man die Notwendigkeit von Kartentabellen, Schreibschutzvorrichtungen usw. beim Speichern von Referenzen vermeiden. Natürlich würde dies nur bei Nutzungsmustern gut funktionieren, die meist unveränderliche Strukturen bevorzugen, aber solche Dinge sind in vielen Anwendungen üblich

0 Stimmen

Hash-Tabellen sind immer noch ein Problem und unveränderliche Wörterbücher sind viel langsamer.

0 Stimmen

Hash-Tabellen und alle darin enthaltenen direkten Verweise müssten also bei jedem GC-Durchgang gescannt werden (wenn die Verweise innerhalb der Tabelle unveränderliche Objekte identifizieren, könnten sie jedoch schnell als ältere Generation erkannt werden). Das wäre immer noch viel billiger als die Anforderung tous Objekte, die bei jedem GC-Durchlauf gescannt werden müssen, was meines Wissens nach vom .NET Micro Framework verlangt wird.

0voto

sharptooth Punkte 162790

Wenn Sie eine große Anzahl von Objekten haben, die nur selten freigegeben werden, wird der Garbage Collector starten und Zeit verschwenden, nur um festzustellen, dass nur wenige Objekte zu beenden sind. In extremen Fällen kann dies zu enormen Leistungseinbußen führen.

0 Stimmen

-1: "In extremen Fällen kann dies zu erheblichen Leistungseinbußen führen". Das glaube ich nicht. Können Sie eine >10-fache Verlangsamung mit einer Produktions-GC nachweisen oder Ihre Behauptung quantifizieren.

0 Stimmen

@Jon Harrop: Interessant. Halten Sie nur > 10x Verlangsamung für ein Problem?

0 Stimmen

Es kommt darauf an, was Sie tun, aber "enorme Leistungseinbußen" bedeutet für mich Größenordnungen.

0voto

Chris Punkte 38327

Wie wäre es aus Sicherheitsgründen? Wenn Sie z. B. einen privaten Verschlüsselungsschlüssel im Speicher haben, möchten Sie ihn wahrscheinlich so kurz wie möglich verfügbar haben.

Abgesehen davon denke ich, dass es sich angesichts der Entwicklung der Hardware mehr lohnt, die Kunst der Multithreading-Programmierung zu erlernen.

1 Stimmen

Free() löscht auch nicht den Speicher.

0 Stimmen

Das ist ein guter Punkt - Sie sollten den Speicher wahrscheinlich auf Null setzen. Having said that, mit unveränderlichen Strings zum Beispiel in C#, es ist eine Herausforderung, es Null, weil der Moment, den Sie die Zeichenfolge ändern, es einfach eine neue Zeichenfolge erstellt. Einige Tricks würden erforderlich sein (oder Sie könnten SecureString verwenden)

0voto

Müllabfuhr vs. Lecks

Einer der Hauptgründe für mich, die Garbage Collection zu vermeiden, ist die Vermeidung von Ressourcenlecks in Bereichen, in denen Lecks kritisch schlecht sind. Garbage Collection ist großartig, wenn Sicherheit und Einfachheit Ihr Ziel ist, aber nicht, um undichte Software zu vermeiden.

Ein häufiges Szenario, auf das wir bei GC gestoßen sind, ist, dass es schwierig ist, Ressourcenlecks zu vermeiden.

Dies mag einige Leute verwirren und paradox erscheinen, da die Garbage Collection in Kombination mit weniger als idealen Teampraktiken zu undichter Software führen kann, aber die nicht-triviale Verwaltung von Ressourcen in einer Software liegt nicht bei den Ressourcen, die an einen begrenzten Bereich gebunden sind, sondern bei den persistenten, die in der Nähe verweilen.

Komplexes Ressourcenmanagement

Ein Beispiel für eine solche Komplexität ist ein Szenengraph in einer 3D-Software mit Millionen von Codezeilen und Tausenden von Objekten und Klassen, die durch Ereignisse miteinander interagieren.

In diesen Fällen speichern diese persistenten Objekte oft Handles/Referenzen auf Ressourcen im System, vielleicht auf andere Objekte, die im persistenten Szenegraphen leben. In solchen Szenarien können Sie eine zentrale Ressource haben, R wie z. B. ein komplexes 3D-Modell, das Hunderte von Megabyte Arbeitsspeicher beansprucht und auf das von vielen verschiedenen Teilen der Szene und der Benutzeroberfläche zugegriffen wird. So könnte beispielsweise sowohl ein Kamera- als auch ein Beleuchtungsobjekt eine Liste von Verweisen auf Objekte speichern, die von der Kameraansicht und dem Beleuchtungssystem auszuschließen sind, wozu auch komplexe 3D-Modelle gehören könnten.

In diesen Fällen und in einer Teamumgebung ist es nicht allzu ungewöhnlich, dass 3 verschiedene Entwickler Code schreiben, der persistente Handles/Referenzen auf R an Dutzenden von verschiedenen Stellen im Code. Wenn der Benutzer entfernt R , todo dieser Stellen sollte den Handle/Reference freigeben.

Ohne Wenn einer von ihnen dies nicht tut (vielleicht hatte er/sie einen schlechten Tag, gehört zu den weniger erfahrenen Entwicklern, stand unter hohem Termindruck mit lockeren Test- und Überprüfungsstandards, was auch immer der Grund sein mag), wird ein baumelndes Zeiger/Handle/Referenz übrig ist. Der Zugriff auf sie wird Absturz die Anwendung mit einem Segfault. Die Verfolgung eines solchen Fehlers mit einem Debugger zeigt oft sofort, wo und warum er aufgetreten ist.

Mit Müllabfuhr, kann nichts Offensichtliches passieren, außer dass die Software über längere Zeiträume läuft und immer mehr Ressourcen verschwendet. Da an einer dieser Stellen vergessen wurde, den Verweis freizugeben und damit seine Lebensdauer dauerhaft zu verlängern und ihn in einem gültigen, nicht zerstörten Zustand weiter zu verwenden, kann es sein, dass die Software nicht nur immer mehr Speicher verbraucht, sondern auch immer langsamer wird, je länger man sie laufen lässt, um versteckte Objekte zu verarbeiten, die für die Benutzer nicht mehr sichtbar sind.

Absturz oder nicht Absturz

In solchen Fällen ist manchmal der offensichtliche und eklatante Absturz, der aus diesem Fehler resultiert, der sofort erkannt und während des Tests behandelt werden kann, tatsächlich vorzugsweise zu einem stillen und sehr schwer zu entdeckenden Ressourcenleck, dessen Aufspüren ein Albtraum sein kann und das möglicherweise nie behoben wird.

Wenn Sie also an einem solchen Projekt arbeiten, bei dem ein sofort offensichtlicher und korrigierbarer Absturz während des Testens einer undichten Software vorzuziehen ist, die mit solchen Fehlern oft unter dem Testradar fliegt, kann Garbage Collection, wenn sie nicht mit sehr sorgfältigen Kodierungsstandards und einem Bewusstsein aller Teammitglieder für ihre Fallstricke (die Notwendigkeit von schwachen oder Phantomreferenzen, z.B.) kombiniert wird, tatsächlich mehr Schaden als Nutzen anrichten. Meiner Meinung nach funktioniert die Garbage Collection am besten in viel kleineren, engeren Teams und bei Projekten mit tatsächlich einem höher nicht ein geringeres Maß an Fachwissen über die Verwaltung von Zuständen/Ressourcen oder solche, bei denen solche Ressourcenverluste nicht annähernd so schlimm sind wie ein Absturz.

Aus der Sicht eines erfahrenen Entwicklers ist ein eklatanter, offensichtlicher, auffälliger Fehler oft besser als ein sehr subtiler, versteckter, Keiner weiß, was passiert ist, aber es ist etwas Schlimmes passiert". Art von Fehlern. Es macht oft den Unterschied aus, ob Ihr Debugger Ihnen sagt, was passiert ist, oder ob Sie blindlings versuchen, Nadeln in einem Heuhaufen von Millionen von Codezeilen zu finden. Die Verwaltung von Ressourcen in großen Systemen ist eine der schwierigsten Aufgaben, die es zu bewältigen gilt, und die Garbage Collection macht das Ganze nicht gerade einfacher. Absturz oder nicht Absturz, das ist die Frage, und in diesen Szenarien haben wir es oft mit dem Dangling-Handle-Absturz ohne GC oder dem mysteriösen Ressourcenleck mit GC zu tun. In leistungskritischen Anwendungen, die mit potenziell enormen Ressourcen umgehen, sind solche Lecks oft inakzeptabel.

-1voto

Boon Punkte 39116

Wenn Sie hochleistungsfähige Anwendungen wie Ego-Shooter-Spiele entwickeln, wollen Sie keinen GC, der die Ausführung Ihrer Anwendung beeinträchtigen könnte. Durch die manuelle Verwaltung der Speicher in diesen Anwendungen können Sie den richtigen Zeitpunkt für die Freigabe von Ressourcen bestimmen.

0 Stimmen

Suchen Sie nach "Incremental Garbage Collection" und besuchen Sie www.lua.org. Achten Sie auf das Logo des Unternehmens auf den Startbildschirmen Ihrer Lieblingsspiele...

0 Stimmen

Also garantiert Lua jetzt die Worst-Case-Sammelzeit?

0 Stimmen

Ihre Kommentare sind fehlgeleitet. Lua ist nur eine Skriptsprache und wird nicht in dem Teil eines Spiels verwendet, der die meiste Leistung erfordert. Zeigen Sie mir ein Ego-Shooter-Spiel, das einen vollwertigen GC-Mechanismus oder Lua zur Implementierung der Rendering-Pipeline oder der Physik verwendet.

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