Warum haben Sprachen wie C und C ++ keine Garbage Collection, während Java dies tut? [geschlossen]


57

Nun, ich weiß, dass es Dinge wie malloc / free für C und new / using-a-destructor für die Speicherverwaltung in C ++ gibt, aber ich habe mich gefragt, warum es für diese Sprachen keine "neuen Aktualisierungen" gibt, die es dem Benutzer ermöglichen Sie haben die Möglichkeit, den Arbeitsspeicher manuell zu verwalten oder das System führt dies automatisch aus (Garbage Collection).

Etwas wie eine neue Frage, aber erst seit ungefähr einem Jahr in CS.


9
In diesem Semester haben wir ein Modul für die iPhone-Entwicklung. Nachdem ich 2 Jahre lang Apps für Android programmiert hatte, traf diese Frage die meisten Schüler ziemlich schwer. Erst jetzt sehen wir, wie viele Stunden uns Java tatsächlich erspart hat, da wir keine bösen Speicherverwaltungsfehler aufspüren und keinen Code für die Kesselplatte schreiben müssen.
Siamii

7
@NullUserException, da es keine Möglichkeit gibt, Speicher zurückzugewinnen, die einen GC impliziert.
Winston Ewert

1
@ bizso09: Hast du ARC schon angeschaut? Keine Notwendigkeit für langsame / fette / nicht deterministische GC, wenn Sie
Systemunterstützung

3
Die Antworten auf diese ziemlich gute Frage stecken voller religiöser Scheiße.
Abatischtschew

1
In C und C ++ ist es möglich, einen Zeiger auf int zu setzen und ihm eine Zahl hinzuzufügen. Subtrahieren Sie später die Zahl vom int und setzen Sie das Ergebnis auf einen Zeiger zurück. Sie erhalten den gleichen Zeiger wie zuvor. Viel Glück beim Implementieren eines GC, der diesen Speicher NICHT sammelt, während seine Adresse nur in der Variablen gespeichert ist, die auch einen anderen Wert hat. Ich weiß, dass das Beispiel albern ist, aber eine XOR-verknüpfte Liste verwendet etwas Ähnliches. Ich würde dies als Antwort posten, aber die Frage ist geschlossen.
Marian Spanik

Antworten:


71

Die Speicherbereinigung erfordert Datenstrukturen für die Verfolgung von Zuordnungen und / oder die Referenzzählung. Dies führt zu einem Mehraufwand an Arbeitsspeicher, Leistung und Komplexität der Sprache. C ++ ist so konzipiert, dass es "nah am Metall" ist, mit anderen Worten, dass es die leistungsstärkere Seite des Kompromisses zu den Komfortfunktionen übernimmt. Andere Sprachen machen diesen Kompromiss anders. Dies ist eine der Überlegungen bei der Auswahl einer Sprache, deren Schwerpunkt Sie bevorzugen.

Allerdings gibt es in C ++ eine Reihe von Referenzzählverfahren, die relativ kompakt und performant sind. Sie befinden sich jedoch in kommerziellen und Open-Source-Bibliotheken und nicht in einem Teil der Sprache. Die Referenzzählung zum Verwalten der Objektlebensdauer ist nicht mit der Speicherbereinigung identisch, sie behebt jedoch viele der gleichen Probleme und passt besser zum grundlegenden Ansatz von C ++.


26
Ein sekundäres Problem ist, dass der GC nicht deterministisch ist. Das Objekt befindet sich möglicherweise noch lange, nachdem das Programm es gelöscht hat, im Speicher. Refcount-Lebenszyklen sind deterministisch. Wenn die letzte Referenz gelöscht wird, wird der Speicher gelöscht. Dies hat Auswirkungen nicht nur auf die Speichereffizienz, sondern auch auf das Debuggen. Ein häufiger Programmierfehler ist das "Zombie" -Objekt, ein Referenzspeicher, der theoretisch gelöscht wurde. Es ist viel wahrscheinlicher, dass GC diesen Effekt überdeckt und zeitweise auftretende und äußerst schwer auffindbare Fehler verursacht.
Kylben

22
- moderne gc's haben weder spurzuordnungen noch zählreferenzen. Sie erstellen ein Diagramm aus allem, was sich aktuell auf dem Stapel befindet, und komprimieren und bereinigen einfach alles andere (vereinfacht), und GC führt normalerweise zu einer geringeren Sprachkomplexität. Auch der Leistungsvorteil ist fraglich.
Joel Coehoorn

13
Er, @kylben, der springende Punkt beim automatischen GC-Backen in der Sprache ist, dass es unmöglich wird , auf Zombie-Objekte zu verweisen, da der GC nur Objekte freigibt, auf die nicht verwiesen werden kann! Wenn Sie bei der manuellen Speicherverwaltung Fehler machen, erhalten Sie die Art von schwer zu verfolgenden Fehlern, von denen Sie sprechen.
Ben

14
-1, GC zählt keine Referenzen. Außerdem kann ein GC abhängig von der Speichernutzung und dem Zuordnungsschema schneller sein (mit einem Mehraufwand bei der Speichernutzung). Das Argument der Leistung ist also auch ein Irrtum. Nur die Nähe zum Metall ist tatsächlich ein gültiger Punkt.
Deadalnix

14
Weder Java noch C # verwenden die Referenzzählung: GC-Schemata, die auf der Referenzzählung basieren, sind im Vergleich ziemlich primitiv und weisen eine viel schlechtere Leistung auf als moderne Garbage Collectors (hauptsächlich, weil sie beim Kopieren einer Referenz Speicherschreibvorgänge ausführen müssen, um die Referenzzählung zu ändern!)
mikera

44

Streng genommen gibt es in der Sprache C überhaupt keine Speicherverwaltung. malloc () und free () sind keine Schlüsselwörter in der Sprache, sondern nur Funktionen, die aus einer Bibliothek aufgerufen werden. Diese Unterscheidung mag jetzt umständlich sein, da malloc () und free () Teil der C-Standardbibliothek sind und von jeder standardkonformen Implementierung von C bereitgestellt werden, aber dies war in der Vergangenheit nicht immer der Fall.

Warum möchten Sie eine Sprache ohne Standard für die Speicherverwaltung? Dies geht auf die Ursprünge von C als „tragbare Baugruppe“ zurück. Es gibt viele Fälle von Hardware und Algorithmen, die von speziellen Speicherverwaltungstechniken profitieren oder diese sogar erfordern können. Soweit ich weiß, gibt es keine Möglichkeit, die native Speicherverwaltung von Java vollständig zu deaktivieren und durch Ihre eigene zu ersetzen. Dies ist in einigen Situationen mit hoher Leistung / minimalen Ressourcen einfach nicht akzeptabel. C bietet nahezu vollständige Flexibilität bei der Auswahl der Infrastruktur, die Ihr Programm verwenden wird. Der Preis ist, dass die Sprache C nur sehr wenig Hilfe beim Schreiben von korrektem, fehlerfreiem Code bietet.


2
+1 eins für die insgesamt gute Antwort, aber auch besonders für "Der Preis ist, dass die C-Sprache sehr wenig Hilfe beim Schreiben von korrektem, fehlerfreiem Code bietet"
Shivan Dragon

2
C verfügt zwar über eine Speicherverwaltung - diese funktioniert jedoch nur, sodass die Benutzer sie kaum bemerken. Es gibt statischen Speicher, Register und den Stapel. Bis Sie anfangen, aus dem Haufen heraus zuzuteilen, sind Sie fein und dandy. Es ist die Heap-Zuordnung, die die Dinge durcheinander bringt. Was Java betrifft, kann jeder seine eigene Java-Laufzeit schreiben - es gibt eine große Auswahl, einschließlich des so genannten "Systems Java". .NET kann so ziemlich alles, was C kann - es bleibt nur hinter den nativen Fähigkeiten von C ++ zurück (z. B. werden Klassen nur in .NET verwaltet). Natürlich haben Sie auch C ++. NET, das alles hat, was C ++ tut, und alles, was .NET tut.
Luaan

1
@Luaan Ich würde sagen, das ist eine sehr großzügige Definition von „Speicherverwaltung“ Ein perfektes Flugzeug, es kann einfach nicht fliegen.
Charles E. Grant

1
@CharlesE.Grant Nun, eine rein funktionale Sprache kann mit dieser Art von Speicherverwaltung alles machen. Nur weil die Heap-Zuweisung in einigen Anwendungsfällen ein guter Kompromiss ist, bedeutet dies nicht, dass dies der Maßstab für alle Sprachen / Laufzeiten ist. Es ist nicht so, dass die Speicherverwaltung aufhört, "Speicherverwaltung" zu sein, nur weil sie einfach, unkompliziert und hinter den Kulissen verborgen ist. Das Entwerfen einer statischen Speicherzuordnung ist nach wie vor Speicherverwaltung, ebenso wie eine gute Verwendung des Stapels und aller anderen verfügbaren Funktionen.
Luaan

"Jede standardkonforme Implementierung" ist nicht wahr, nur für die standardkonforme Implementierung der Host-Umgebung. Einige Plattformen / Standardbibliotheken, die meisten für eingebettete 8- oder 16-Bit-Mikrocontroller, bieten malloc()oder nicht an free(). (Beispiel sind MLAP-Compiler für PIC)
12431234123412341234123

32

Die eigentliche Antwort lautet, dass der einzige Weg, um einen sicheren und effizienten Mechanismus für die Garbage Collection zu schaffen, die Unterstützung von undurchsichtigen Referenzen auf Sprachebene ist . (Oder umgekehrt, mangelnde Sprachunterstützung für direkte Speichermanipulation.)

Java und C # können dies, weil sie spezielle Referenztypen haben, die nicht manipuliert werden können. Dies gibt der Laufzeit die Freiheit, beispielsweise zugewiesene Objekte im Speicher zu verschieben , was für eine leistungsstarke GC-Implementierung von entscheidender Bedeutung ist.

Für die Aufzeichnung verwendet keine moderne GC-Implementierung Referenzzählung , so dass es sich vollständig um einen roten Hering handelt. Moderne GCs verwenden die Generationssammlung, bei der neue Zuordnungen im Wesentlichen genauso behandelt werden wie Stapelzuordnungen in einer Sprache wie C ++, und neu zugewiesene Objekte, die noch am Leben sind, in regelmäßigen Abständen in einen separaten "Überlebensraum" und in eine gesamte Generation verschoben werden von Objekten wird sofort freigegeben.

Dieser Ansatz hat Vor- und Nachteile: Der Vorteil ist, dass Heap-Zuweisungen in einer Sprache, die GC unterstützt, genauso schnell sind wie Stack-Zuweisungen in einer Sprache, die GC nicht unterstützt, und der Nachteil ist, dass Objekte bereinigt werden müssen, bevor sie zerstört werden erfordern einen separaten Mechanismus (z. B. das usingSchlüsselwort von C # ) oder ihr Bereinigungscode wird nicht deterministisch ausgeführt.

Beachten Sie, dass ein Schlüssel zu einem Hochleistungs-GC darin besteht, dass eine spezielle Klasse von Referenzen sprachlich unterstützt werden muss. C hat diese Sprachunterstützung nicht und wird es auch nie tun. Da C ++ eine Überladung von Operatoren aufweist, könnte es einen GC-Zeigertyp emulieren, obwohl dies sorgfältig durchgeführt werden müsste. Tatsächlich mussten Microsoft, als sie ihren C ++ - Dialekt erfanden, der unter der CLR (der .NET-Laufzeit) ausgeführt wurde, eine neue Syntax für "C # -Stilreferenzen" erfinden Foo^, um sie von "C ++ - Stilreferenzen" zu unterscheiden. (zB Foo&).

Was C ++ hat und was von C ++ - Programmierern regelmäßig verwendet wird, sind intelligente Zeiger , die eigentlich nur ein Referenzzählmechanismus sind. Ich würde die Referenzzählung nicht als "echte" GC betrachten, aber sie bietet viele der gleichen Vorteile auf Kosten einer geringeren Leistung als die manuelle Speicherverwaltung oder die echte GC, jedoch mit dem Vorteil einer deterministischen Zerstörung.

Am Ende des Tages läuft die Antwort wirklich auf ein Sprachgestaltungsmerkmal hinaus. C hat eine Wahl getroffen, C ++ hat eine Wahl getroffen, die es ermöglicht, mit C abwärtskompatibel zu sein, während es weiterhin Alternativen bietet, die für die meisten Zwecke gut genug sind, und Java und C # haben eine andere Wahl getroffen, die inkompatibel mit C ist, aber auch gut genug für die meisten Zwecke. Leider gibt es kein Patentrezept, aber wenn Sie mit den verschiedenen Auswahlmöglichkeiten vertraut sind, können Sie für jedes Programm, das Sie gerade erstellen möchten, das richtige auswählen.


4
Dies ist die eigentliche Antwort auf die Frage
Coredump

1
Für den c ++ - Teil sollten Sie sich heutzutage std :: unique_ptr und std :: move ansehen :)
Niclas Larsson

@NiclasLarsson: Ich bin mir nicht sicher, ob ich deinen Standpunkt verstehe. Wollen Sie damit sagen, dass dies std::unique_ptr"Sprachunterstützung für undurchsichtige Referenzen" ist? (Es war nicht die Art von Unterstützung, die ich meinte, und ich denke auch nicht, dass dies ausreicht, es sei denn, die Unterstützung für die direkte Speichermanipulation wurde auch aus C ++ entfernt.) In meiner Antwort erwähne ich intelligente Zeiger, und ich würde std:unique_ptreinen intelligenten Zeiger in Betracht ziehen Da die Referenzzählung tatsächlich durchgeführt wird, werden nur die Sonderfälle unterstützt, in denen die Anzahl der Referenzen entweder Null oder Eins ist (und std::moveder Mechanismus zur Aktualisierung der Referenzzählung).
Daniel Pryden

std::unique_ptrhat keinen Referenzzähler und std::movehat überhaupt nichts mit Referenzen zu tun (also "kein" Leistungseinbruch). Ich verstehe Ihren Standpunkt jedoch, da std::shared_ptres eine Referenzzählung gibt, die implizit von std::move:) aktualisiert wird
Niclas Larsson,

2
@ Mike76: Auf der Zuweisungsseite arbeitet ein GC-Zuweiser ungefähr so ​​schnell wie die Stapelzuweisung, und der GC kann Tausende von Objekten gleichzeitig freigeben. Egal, was Sie mit einer Ref-Counting-Implementierung tun, die Zuordnung und Freigabe wird niemals schneller als mallocund sein free. Also ja, ein GC kann wesentlich schneller sein. (Beachten Sie, dass ich sagte "kann sein" - natürlich wird die genaue Leistung jedes Programms von vielen Faktoren beeinflusst.)
Daniel Pryden

27

Denn wenn Sie die Leistungsfähigkeit von C ++ nutzen, ist dies nicht erforderlich.

Herb Sutter: " Ich habe seit Jahren nichts mehr geschrieben. "

Siehe Schreiben von modernem C ++ - Code: Wie sich C ++ im Laufe der Jahre um 21:10 Uhr entwickelt hat

Es mag viele erfahrene C ++ - Programmierer überraschen.


Interessant. Mein Lesematerial für heute.
Surfasb

Bah, ein Video. Aber trotzdem schon interessant.
Surfasb

2
interessantes Video. 21 Minuten und 55 Minuten waren die besten Punkte. Schade, dass die WinRT-Aufrufe immer noch C ++ / CLI-Bumpf waren.
gbjbaanb

2
@ dan04: Das stimmt. Aber dann, wenn Sie in C schreiben, bekommen Sie, wonach Sie fragen.
DeadMG

6
Das Verwalten der intelligenten Zeiger ist nicht anspruchsvoller als sicherzustellen, dass in einer Umgebung mit Speicherbereinigung keine unnötigen Verweise vorhanden sind. Da GC Ihre Gedanken nicht lesen kann, ist es auch keine Magie.
Tamás Szelei

15

"Alle" Ein Garbage Collector ist ein Prozess, der regelmäßig überprüft, ob sich nicht referenzierte Objekte im Speicher befinden und ob sie gelöscht werden. (Ja, ich weiß, dass dies eine grobe Vereinfachung ist). Dies ist keine Eigenschaft der Sprache, sondern des Frameworks.

Es gibt Garbage Collectors, die für C und C ++ geschrieben wurden - dieses Beispiel.

Ein Grund, warum man der Sprache nicht "hinzugefügt" wurde, könnte sein, dass es so viel vorhandenen Code gibt, der ihn niemals verwenden würde, da er seinen eigenen Code für die Speicherverwaltung verwendet. Ein weiterer Grund könnte sein, dass für die in C und C ++ geschriebenen Anwendungstypen der mit einem Garbage Collection-Prozess verbundene Overhead nicht erforderlich ist.


1
Aber zukünftige Programme, die geschrieben wurden, würden anfangen, den Garbage Collector zu benutzen, nicht wahr?
Dunkle Templer

5
Während die Garbage Collection theoretisch von jeder Programmiersprache unabhängig ist, ist es ziemlich schwierig, einen nützlichen GC für C / C ++ zu schreiben, und es ist sogar unmöglich, einen narrensicheren (mindestens so narrensicher wie den von Java) zu erstellen - der Grund, warum Java diesen abrufen kann Aus ist, weil es in einer kontrollierten virtualisierten Umgebung ausgeführt wird. Umgekehrt ist die Java-Sprache für GC konzipiert, und es fällt Ihnen schwer, einen Java-Compiler zu schreiben, der GC nicht ausführt.
Tdammers

4
@tdammers: Ich stimme zu, dass die Garbage-Collection von der Sprache unterstützt werden muss, um möglich zu sein. Der wichtigste Punkt ist jedoch nicht die Virtualisierung und die kontrollierte Umgebung, sondern die strikte Typisierung. C und C ++ sind schwach typisiert, sodass Zeiger beispielsweise in Ganzzahlvariablen gespeichert, Zeiger aus Offsets rekonstruiert und verhindert werden, dass der Kollektor zuverlässig erkennt, was erreichbar ist (C ++ 11 verhindert, dass der Kollektor dies später zulässt) konservative Sammler). In Java wissen Sie immer, was eine Referenz ist, sodass Sie sie präzise erfassen können, auch wenn sie in native kompiliert wurde.
Jan Hudec

2
@ ThorbjørnRavnAndersen: Ich kann ein gültiges C-Programm schreiben, das Zeiger so speichert, dass kein Müllmann sie jemals finden kann. Wenn Sie dann einen Garbage Collector an mallocund anschließen free, würden Sie mein korrektes Programm unterbrechen.
Ben Voigt

2
@ ThorbjørnRavnAndersen: Nein, ich würde erst anrufen, freewenn ich damit fertig wäre. Aber Ihr vorgeschlagener Garbage Collector, der den Speicher erst freigibt, wenn ich ihn explizit aufrufe, freeist überhaupt kein Garbage Collector.
Ben Voigt

12

C wurde in einer Zeit entwickelt, in der die Müllabfuhr kaum in Frage kam. Es war auch für Anwendungen gedacht, bei denen die Garbage Collection im Allgemeinen nicht funktioniert - Bare Metal, Echtzeitumgebungen mit minimalem Arbeitsspeicher und minimaler Laufzeitunterstützung. Denken Sie daran, dass C die Implementierungssprache für das erste Unix war, das auf einem pdp-11 mit 64 * K * Bytes Speicher lief. C ++ war ursprünglich eine Erweiterung von C - die Wahl wurde bereits getroffen, und es ist sehr schwierig, eine Garbage Collection auf eine vorhandene Sprache zu übertragen. So etwas muss vom Erdgeschoss aus eingebaut werden.


9

Ich habe nicht die genauen Zitate, aber Bjarne und Herb Sutter sagen etwas in die richtige Richtung:

C ++ benötigt keinen Garbage Collector, da es keinen Garbage hat.

In modernem C ++ verwenden Sie intelligente Zeiger und haben daher keinen Müll.


1
Was sind intelligente Zeiger?
Dunkle Templer

11
Wenn es so einfach wäre, hätte niemand einen GC implementiert.
Deadalnix

7
@deadalnix: Richtig, denn niemand implementiert jemals etwas übermäßig Kompliziertes, Langsames, Aufgedunsenes oder Unnötiges. Jede Software ist immer zu 100% effizient, oder?
Zach

5
@deadalnix - Der C ++ - Ansatz zur Speicherverwaltung ist neuer als Garbage Collectors. RAII wurde von Bjarne Stroustrup für C ++ erfunden. Destruktor-Bereinigung ist eine ältere Idee, aber die Regeln zur Gewährleistung der Ausnahmesicherheit sind entscheidend. Ich weiß nicht genau, wann die Idee selbst zum ersten Mal beschrieben wurde, aber der erste C ++ - Standard wurde 1998 fertiggestellt und Stroustrups "Design and Evolution of C ++" wurde erst 1994 veröffentlicht, und Ausnahmen waren eine relativ neue Ergänzung zu C ++ - Nach der Veröffentlichung des "Annotated C ++ Reference Manual" im Jahr 1990 glaube ich. GC wurde 1959 für Lisp erfunden.
Steve314

1
@deadalnix - Ist Ihnen bekannt, dass mindestens eine Java-VM einen Referenzzähl-GC verwendet hat, der (fast) mithilfe von C ++ - RAII mit einer Smart-Pointer-Klasse implementiert werden konnte - gerade weil er für Multithread-Code effizienter war als vorhandene VMs? Siehe www.research.ibm.com/people/d/dfb/papers/Bacon01Concurrent.pdf. Ein Grund, warum Sie dies in C ++ in der Praxis nicht sehen, ist die übliche GC-Sammlung - sie kann Zyklen sammeln, kann jedoch keine sichere Destruktorreihenfolge bei vorhandenen Zyklen wählen und kann daher keine zuverlässige Destruktorbereinigung gewährleisten.
Steve314

8

Sie fragen, warum diese Sprachen nicht mit einem optionalen Garbage Collector aktualisiert wurden.

Das Problem bei der optionalen Garbage Collection ist, dass Sie keinen Code mischen können, der die verschiedenen Modelle verwendet. Das heißt, wenn ich Code schreibe, der davon ausgeht, dass Sie einen Garbage Collector verwenden, können Sie ihn nicht in Ihrem Programm verwenden, in dem die Garbage Collection deaktiviert ist. Wenn Sie dies tun, wird es überall lecken.


6

Können Sie sich vorstellen, einen Device-Handler in einer Sprache mit Garbage Collection zu schreiben? Wie viele Bits könnten die Leitung herunterkommen, während der GC lief?

Oder ein Betriebssystem? Wie können Sie die Garbage Collection starten, bevor Sie überhaupt den Kernel starten?

C ist für niedrige Pegel in der Nähe der Hardware-Aufgaben ausgelegt. Das Problem? Ist es eine so schöne Sprache, dass es auch eine gute Wahl für viele übergeordnete Aufgaben ist? Die Sprachzaren kennen diese Verwendungen, müssen jedoch vorrangig die Anforderungen von Gerätetreibern, Embedded Code und Betriebssystemen erfüllen.


2
C gut für hohes Niveau? Ich schnaubte mein Getränk über meine Tastatur.
DeadMG

5
Nun, er sagte "viele Aufgaben auf höherer Ebene". Er könnte Troll zählen (eins, zwei, viele ...). Und er sagte eigentlich nicht mehr als was. Scherz beiseite, aber es ist wahr - der Beweis ist , dass viele bedeutende geordnete Projekte haben erfolgreich in C entwickelt Es kann jetzt für viele dieser Projekte eine bessere Wahl sein, aber ein Arbeitsprojekt ist stärker Beweise als Spekulation darüber , was könnte gewesen sein.
Steve314

Es gibt einige verwaltete Betriebssysteme, die recht gut funktionieren. Wenn Sie das gesamte System verwalten, sinkt die Leistung durch die Verwendung von verwaltetem Code sogar noch weiter und ist in realen Szenarien schneller als nicht verwalteter Code. Natürlich sind das alles "Research OS" - es gibt so gut wie keine Möglichkeit, sie mit vorhandenem, nicht verwaltetem Code kompatibel zu machen, außer ein vollständig virtualisiertes, nicht verwaltetes Betriebssystem innerhalb des verwalteten Betriebssystems zu erstellen. Microsoft hat jedoch irgendwann vorgeschlagen, Windows Server durch einen solchen zu ersetzen, da immer mehr Servercode in .NET geschrieben wird.
Luaan

6

Die kurze und langweilige Antwort auf diese Frage ist, dass es eine Sprache geben muss, die keine Müllsammler sind. Es ist konzeptionell nicht einfach, eine Sprache zu haben, die gleichzeitig eine sehr genaue Kontrolle über das Speicherlayout ermöglicht und auf der ein GC ausgeführt wird.

Die andere Frage ist, warum C und C ++ keine Garbage Collectors haben. Ich weiß, dass C ++ ein paar davon hat, aber sie sind nicht sehr beliebt, weil sie gezwungen sind, sich mit einer Sprache zu befassen, die ursprünglich nicht für GC-Eds entwickelt wurde, und mit den Leuten, die C ++ noch verwenden Dieses Alter ist nicht wirklich die Art, die einen GC vermisst.

Anstatt GC zu einer alten Nicht-GC-Sprache hinzuzufügen, ist es tatsächlich einfacher, eine neue Sprache mit der gleichen Syntax zu erstellen, während ein GC unterstützt wird. Java und C # sind hierfür gute Beispiele.


1
Irgendwo auf programers.se oder SO wird behauptet, jemand habe an einem Garbage-Collected-Ding mit Selbst-Bootstrapping gearbeitet - IIRC implementiert die VM im Grunde unter Verwendung einer GC-Sprache, wobei eine Bootstrapping-Teilmenge zur Implementierung des GC selbst verwendet wird. Ich habe den Namen vergessen. Als ich es mir ansah, stellte sich heraus, dass sie im Grunde nie den Sprung von der Untergruppe ohne GC zur Arbeits-GC-Ebene geschafft hatten. Dies ist prinzipiell möglich, aber AFAIK hat es in der Praxis nie geschafft - es geht sicherlich darum, Dinge auf die harte Tour zu machen.
Steve314

@ Steve314: Ich würde gerne sehen, ob du dich jemals daran erinnerst, wo du es gefunden hast!
hugomg

fand es! Siehe die Kommentare zu stackoverflow.com/questions/3317329/… bezüglich der Klein VM. Ein Teil des Problems, es zu finden - die Frage wurde geschlossen.
Steve314

BTW - Ich kann meine Kommentare anscheinend nicht mit @missingno beginnen - was gibt es?
Steve314

@ steve314: Nachdem ich die Antwort gefunden habe, an die dieser Thread angehängt ist, erhalte ich bereits eine Benachrichtigung für alle Kommentare. In diesem Fall wäre ein @ -Post überflüssig und von SE nicht erlaubt (frag mich aber nicht warum ). (Die wahre Ursache ist jedoch, dass meine Nummer fehlt)
hugomg

5

Es gibt verschiedene Probleme, darunter ...

  • Obwohl GC vor C ++ und möglicherweise vor C erfunden wurde, wurden sowohl C als auch C ++ implementiert, bevor GCs allgemein als praktisch akzeptiert wurden.
  • Sie können eine GC-Sprache und -Plattform nicht ohne eine zugrunde liegende Nicht-GC-Sprache implementieren.
  • Obwohl GC für typischen Anwendungscode, der in typischen Zeitskalen usw. entwickelt wurde, nachweislich effizienter ist als Nicht-GC, gibt es Probleme, bei denen mehr Entwicklungsaufwand ein guter Kompromiss ist und die spezialisierte Speicherverwaltung einen Allzweck-GC übertrifft. Außerdem ist C ++ auch ohne zusätzlichen Entwicklungsaufwand nachweislich effizienter als die meisten GC-Sprachen.
  • GC ist nicht universell sicherer als C ++ - ähnliches RAII. Mit RAII können andere Ressourcen als der Arbeitsspeicher automatisch bereinigt werden, da zuverlässige und zeitnahe Destruktoren unterstützt werden. Diese können aufgrund von Problemen mit Referenzzyklen nicht mit herkömmlichen GC-Methoden kombiniert werden.
  • GC-Sprachen weisen ihre eigenen charakteristischen Arten von Speicherverlusten auf, insbesondere in Bezug auf Speicher, die nie wieder verwendet werden, bei denen jedoch vorhandene Referenzen vorhanden waren, die nie gelöscht oder überschrieben wurden. Die Notwendigkeit, dies explizit zu tun, unterscheidet sich grundsätzlich nicht von der Notwendigkeit deleteoder freeexplizit. Der GC-Ansatz hat immer noch einen Vorteil - keine herabhängenden Referenzen - und die statische Analyse kann einige Fälle erfassen, aber auch hier gibt es keine perfekte Lösung für alle Fälle.

Im Grunde geht es zum Teil um das Alter der Sprachen, aber es wird sowieso immer einen Platz für Nicht-GC-Sprachen geben - auch wenn es ein bisschen ein Nischenplatz ist. Und im Ernst, in C ++ ist das Fehlen von GC keine große Sache - Ihr Speicher wird anders verwaltet, ist aber nicht unmanaged.

Microsoft Managed C ++ kann GC und Nicht-GC in ein und derselben Anwendung mischen, sodass die jeweiligen Vorteile miteinander kombiniert werden können. Ich habe jedoch nicht die Erfahrung, zu beurteilen, wie gut dies in der Praxis funktioniert.

Rep-Whoring-Links zu verwandten Antworten von mir ...


4

Die Garbage Collection ist grundsätzlich nicht mit einer Systemsprache kompatibel, die zum Entwickeln von Treibern für DMA-fähige Hardware verwendet wird.

Es ist durchaus möglich, dass der einzige Zeiger auf ein Objekt in einem Hardware-Register in einem Peripheriegerät gespeichert wird. Da der Müllsammler davon nichts wissen würde, würde er das Objekt für unerreichbar halten und es einsammeln.

Dieses Argument gilt doppelt für die Verdichtung von GC. Selbst wenn Sie darauf geachtet haben, speicherinterne Verweise auf von Hardware-Peripheriegeräten verwendete Objekte beizubehalten, wusste der GC beim Verschieben des Objekts nicht, wie der im Peripherie-Konfigurationsregister enthaltene Zeiger aktualisiert werden sollte.

Jetzt brauchen Sie also eine Mischung aus unbeweglichen DMA-Puffern und GC-verwalteten Objekten, was bedeutet, dass Sie alle Nachteile von beiden haben.


Vermutlich alle Nachteile von beiden, aber weniger Instanzen von jedem Nachteil und die gleichen für die Vorteile. Es ist klar, dass es kompliziert ist, mehr Arten der Speicherverwaltung zu bewältigen, es kann jedoch auch Komplexität vermieden werden, wenn Sie für jeden Kurs in Ihrem Code das richtige Pferd auswählen. Ich stelle mir das unwahrscheinlich vor, aber es gibt eine theoretische Lücke. Ich habe darüber spekuliert, GC und Nicht-GC in derselben Sprache zu mischen, aber nicht für Gerätetreiber - mehr für eine hauptsächlich GC-Anwendung, aber mit einigen manuell speicherverwalteten Low-Level-Datenstrukturbibliotheken.
Steve314

@ Steve314: Würden Sie nicht sagen, dass es genauso mühsam ist, sich zu erinnern, welche Objekte manuell freigegeben werden müssen, wie sich daran zu erinnern, alles freizugeben? (Natürlich können auch intelligente Zeiger Abhilfe schaffen, sodass keines der beiden Probleme ein großes Problem darstellt.) Außerdem benötigen Sie unterschiedliche Pools für manuell verwaltete Objekte im Vergleich zu gesammelten / komprimierbaren Objekten, da die Komprimierung nicht gut funktioniert, wenn feste Objekte über alle Bereiche verteilt sind. So viel zusätzliche Komplexität für nichts.
Ben Voigt

2
Nicht, wenn es eine klare Trennung zwischen dem High-Level-Code, bei dem es sich ausschließlich um GC handelt, und dem Low-Level-Code gibt, bei dem kein GC ausgeführt wird. Ich habe die Idee vor einigen Jahren hauptsächlich beim Betrachten von D entwickelt, wodurch Sie GC deaktivieren, aber nicht wieder aktivieren können. Nehmen Sie zum Beispiel eine B + -Baumbibliothek. Der Container als Ganzes sollte GC sein, die Datenstrukturknoten jedoch wahrscheinlich nicht. Es ist effizienter, nur die Blattknoten benutzerdefiniert zu durchsuchen, als den GC zu veranlassen, eine rekursive Suche durch die Zweigknoten durchzuführen. Dieser Scan muss jedoch die enthaltenen Elemente an den GC melden.
Steve314

Der Punkt ist, dass dies ein Teil der Funktionalität ist. Die Behandlung der B + Baumknoten als Sonder WRT Speicherverwaltung ist nicht anders als Sondermüll WRT zu behandeln ist B + Baumknoten. Es ist eine gekapselte Bibliothek, und der Anwendungscode muss nicht wissen, dass das GC-Verhalten umgangen wurde. Abgesehen davon, dass dies zumindest zu diesem Zeitpunkt in D unmöglich war - wie gesagt, es war nicht möglich, die enthaltenen Elemente wieder zu aktivieren und dem GC als potenzielle GC-Roots zu melden.
Steve314

3

Denn C & C ++ sind Sprachen mit relativ niedrigem Sprachniveau, die für allgemeine Zwecke gedacht sind, auch um beispielsweise auf einem 16-Bit-Prozessor mit 1 MB Speicher in einem eingebetteten System ausgeführt zu werden, der es sich nicht leisten konnte, Speicher mit gc zu verschwenden.


1
"Eingebettetes System"? Zu der Zeit, als C standardisiert wurde (1989), musste es in der Lage sein, PCs mit 1 MB Arbeitsspeicher zu handhaben .
Dan04

Ich stimme zu, ich habe ein aktuelleres Beispiel angeführt.
Petruza

1 MB ??? Holy schmoley, wer würde jemals so viel RAM brauchen? </ billGates>
Mark K Cowan

2

In C ++ und C gibt es Garbage Collectors. Sie sind sich nicht sicher, wie dies in C funktioniert. In C ++ können Sie jedoch RTTI verwenden , um Ihr Objektdiagramm dynamisch zu erkennen und für die Garbage Collection zu verwenden.

Meines Wissens können Sie Java nicht ohne einen Garbage Collector schreiben. Eine kleine Suche ergab dies .

Der Hauptunterschied zwischen Java und C / C ++ besteht darin, dass Sie in C / C ++ immer die Wahl haben, wohingegen Sie in Java häufig aufgrund des Designs keine Optionen haben.


Und auch, dass die dedizierten Garbage Collectors besser implementiert werden, effizienter sind und besser in die Sprache passen. :)
Max

Nein, Sie können RTTI nicht verwenden, um das Objektdiagramm in C / C ++ dynamisch zu erkennen: Es sind die einfachen alten Datenobjekte, die alles verderben. In einem einfachen alten Datenobjekt sind einfach keine RTTI-Informationen gespeichert, die es einem Garbage Collector ermöglichen würden, zwischen Zeigern und Nicht-Zeigern innerhalb dieses Objekts zu unterscheiden. Schlimmer noch, Zeiger müssen nicht auf jeder Hardware perfekt ausgerichtet sein. Bei einem 16-Byte-Objekt gibt es also 9 mögliche Positionen, an denen ein 64-Bit-Zeiger gespeichert werden kann, von denen sich nur zwei nicht überlappen.
cmaster

2

Es ist ein Kompromiss zwischen Leistung und Sicherheit.

Es gibt keine Garantie dafür, dass Ihr Müll in Java gesammelt wird. Daher kann es sein, dass er über einen längeren Zeitraum hinweg Speicherplatz beansprucht. Das Scannen nach nicht referenzierten Objekten (z. B. Müll) dauert jedoch länger als das explizite Löschen oder Freigeben eines nicht verwendeten Objekts.

Der Vorteil ist natürlich, dass man eine Sprache ohne Zeiger oder ohne Speicherlecks erstellen kann, so dass man mit größerer Wahrscheinlichkeit korrekten Code erzeugt.

Diese Debatten können manchmal einen gewissen "religiösen" Rand haben - seien Sie gewarnt!


1

Hier ist eine Liste der inhärenten Probleme von GC, die es in einer Systemsprache wie C unbrauchbar machen:

  • Der GC muss unter der Ebene des Codes ausgeführt werden, dessen Objekte er verwaltet. Es gibt einfach keine solche Ebene in einem Kernel.

  • Ein GC muss den verwalteten Code von Zeit zu Zeit stoppen. Nun überlegen Sie, was passieren würde, wenn es Ihrem Kernel so ginge. Die gesamte Verarbeitung auf Ihrem Computer wird beispielsweise für eine Millisekunde angehalten, während der GC alle vorhandenen Speicherzuordnungen überprüft. Dies würde alle Versuche, Systeme zu erstellen, die unter strengen Echtzeitanforderungen arbeiten, zunichte machen.

  • Ein GC muss in der Lage sein, zwischen Zeigern und Nichtzeigern zu unterscheiden. Das heißt, es muss in der Lage sein, jedes existierende Speicherobjekt zu betrachten und eine Liste von Offsets zu erstellen, in denen sich seine Zeiger befinden.

    Diese Entdeckung muss perfekt sein: Der GC muss in der Lage sein, alle gefundenen Hinweise zu verfolgen. Wenn es ein falsches Positiv dereferenziert, würde es wahrscheinlich abstürzen. Wenn ein falsches Negativ nicht erkannt wird, wird wahrscheinlich ein noch verwendetes Objekt zerstört, der verwaltete Code stürzt ab oder die Daten werden unbemerkt beschädigt.

    Dies setzt unbedingt voraus, dass in jedem existierenden Objekt eine Typinformation hinterlegt ist. Sowohl C als auch C ++ ermöglichen jedoch einfache alte Datenobjekte, die keine Typinformationen enthalten.

  • GC ist von Natur aus ein langsames Geschäft. Mit Java sozialisierte Programmierer erkennen dies möglicherweise nicht, aber Programme können um Größenordnungen schneller sein, wenn sie nicht in Java implementiert sind. Und einer der Faktoren, die Java langsam machen, ist GC. Dies verhindert, dass GCed-Sprachen wie Java im Supercomputing verwendet werden. Wenn Ihre Maschine eine Million pro Jahr Strom kostet, möchten Sie nicht einmal 10% davon für die Müllabfuhr bezahlen.

C und C ++ sind Sprachen, die zur Unterstützung aller möglichen Anwendungsfälle erstellt wurden. Und wie Sie sehen, sind viele dieser Anwendungsfälle durch die Garbage Collection ausgeschlossen. Um diese Anwendungsfälle zu unterstützen, kann in C / C ++ kein Müll gesammelt werden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.