it-swarm-eu.dev

Warum haben Sprachen wie C und C ++ keine Garbage Collection, während Java)?

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 Updates" gibt, die es dem Benutzer ermöglichen Haben Sie die Möglichkeit, den Speicher manuell zu verwalten oder das System dies automatisch zu tun (Speicherbereinigung)?

Etwas neuartig, aber erst seit ungefähr einem Jahr in CS.

58
Dark Templar

Die Speicherbereinigung erfordert Datenstrukturen zum Verfolgen von Zuordnungen und/oder Referenzzählungen. Diese verursachen Speicheraufwand, Leistung und die Komplexität der Sprache. C++ ist so konzipiert, dass es "nah am Metall" ist, mit anderen Worten, es bietet die leistungsstärkere Seite des Kompromisses gegenüber den Komfortfunktionen. Andere Sprachen machen diesen Kompromiss anders. Dies ist eine der Überlegungen bei der Auswahl einer Sprache, welche Betonung Sie bevorzugen.

Trotzdem gibt es in C++ viele Schemata für die Referenzzählung, die recht leicht und leistungsfähig sind, sich jedoch in kommerziellen und Open Source-Bibliotheken befinden und nicht Teil der Sprache selbst sind. Die Referenzzählung zum Verwalten der Objektlebensdauer ist nicht mit der Garbage Collection identisch, behebt jedoch viele der gleichen Probleme und passt besser zum grundlegenden Ansatz von C++.

72
kylben

Genau genommen gibt es in der C-Sprache ü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 pedantisch sein, da malloc () und free () Teil der C-Standardbibliothek sind und von jeder standardkonformen Implementierung von C bereitgestellt werden. Dies war jedoch in der Vergangenheit nicht immer der Fall.

Warum möchten Sie eine Sprache ohne Standard für die Speicherverwaltung? Dies geht auf Cs Ursprünge 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 und minimalen Ressourcen einfach nicht akzeptabel. C bietet nahezu vollständige Flexibilität, um genau zu wählen, welche Infrastruktur Ihr Programm verwenden soll. Der gezahlte Preis ist, dass die C-Sprache nur sehr wenig Hilfe beim Schreiben von korrektem, fehlerfreiem Code bietet.

44

Die eigentliche Antwort lautet, dass die einzige Möglichkeit, einen sicheren und effizienten Speicherbereinigungsmechanismus zu erstellen, darin besteht, nterstützung auf Sprachebene für undurchsichtige Referenzen zu haben. (Oder umgekehrt ein Mangel an Unterstützung auf Sprachebene für die direkte Manipulation des Speichers.)

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

Für die Aufzeichnung keine moderne GC-Implementierung verwendet Referenzzählung, so dass dies vollständig ein roter Hering ist. Moderne GCs verwenden die Generationssammlung, bei der neue Zuordnungen im Wesentlichen genauso behandelt werden wie Stapelzuweisungen in einer Sprache wie C++. Anschließend werden regelmäßig neu zugewiesene Objekte, die noch am Leben sind, in einen separaten "Überlebensbereich" und eine gesamte Generation verschoben von Objekten wird sofort freigegeben.

Dieser Ansatz hat Vor- und Nachteile: Der Vorteil ist, dass Heap-Zuweisungen in einer Sprache, die GC unterstützt, so schnell wie Stapelzuweisungen in einer Sprache sind, die GC nicht unterstützt, und der Nachteil sind die Objekte, die benötigt werden Um eine Bereinigung durchzuführen, bevor sie zerstört wird, ist entweder ein separater Mechanismus erforderlich (z. B. das Schlüsselwort using von C #), oder der Bereinigungscode wird nicht deterministisch ausgeführt.

Beachten Sie, dass ein Schlüssel zu einem Hochleistungs-GC darin besteht, dass eine spezielle Referenzklasse sprachlich unterstützt werden muss. C hat diese Sprachunterstützung nicht und wird es niemals tun. Da C++ über eine Operatorüberladung verfügt, 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 werden sollte, eine neue Syntax für "C # -Stilreferenzen" erfinden (z. B. Foo^), um sie von "Referenzen im C++ - Stil" zu unterscheiden (z. B. Foo&).

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

Letztendlich läuft die Antwort wirklich auf ein Sprachdesign-Feature hinaus. C traf eine Wahl, C++ traf eine Wahl, die es ermöglichte, abwärtskompatibel mit C zu sein und gleichzeitig Alternativen bereitzustellen, die für die meisten Zwecke gut genug sind, und Java und C # trafen eine andere Wahl Inkompatibel mit C, aber auch für die meisten Zwecke gut genug. Leider gibt es keine Silberkugel, aber wenn Sie mit den verschiedenen Auswahlmöglichkeiten vertraut sind, können Sie die richtige für jedes Programm auswählen, das Sie gerade erstellen möchten.

32
Daniel Pryden

Denn wenn Sie die Leistung von C++ nutzen, besteht keine Notwendigkeit.

Herb Sutter: " Ich habe seit Jahren kein Löschen mehr geschrieben. "

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

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

27
Lior Kogan

"Alles", was ein Garbage Collector ist, ist ein Prozess, der regelmäßig ausgeführt wird, um zu überprüfen, 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 zum Beispiel für C und C++ geschrieben wurden - dieser .

Ein Grund, warum man der Sprache nicht "hinzugefügt" wurde, könnte das schiere Volumen des vorhandenen Codes sein, der ihn niemals verwenden würde, da er seinen eigenen Code zum Verwalten des Speichers verwendet. Ein weiterer Grund könnte sein, dass die in C und C++ geschriebenen Anwendungstypen nicht den mit einem Garbage Collection-Prozess verbundenen Overhead benötigen.

15
ChrisF

C wurde in einer Zeit entwickelt, in der die Müllabfuhr kaum eine Option war. Es war auch für Anwendungen gedacht, bei denen die Speicherbereinigung im Allgemeinen nicht funktioniert - Bare-Metal-Umgebungen in Echtzeit 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, die Speicherbereinigung auf eine vorhandene Sprache zu übertragen. So etwas muss vom Erdgeschoss aus eingebaut werden.

12
ddyer

Ich habe nicht die genauen Zitate, aber sowohl Bjarne als auch Herb Sutter sagen etwas in der 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.

9
ronag

Sie fragen, warum diese Sprachen nicht aktualisiert wurden, um einen optionalen Garbage Collector einzuschließen.

Das Problem bei der optionalen Speicherbereinigung besteht darin, 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 auslaufen.

8
Winston Ewert

Die kurze und langweilige Antwort auf diese Frage lautet, dass es für die Leute, die die Müllsammler schreiben, eine Sprache geben muss, in der kein Müll gesammelt wird. Es ist konzeptionell nicht einfach, eine Sprache zu haben, die gleichzeitig eine sehr genaue Steuerung des Speicherlayouts ermöglicht nd auf dem ein GC ausgeführt wird.

Die andere Frage ist, warum C und C++ keine Garbage Collectors haben. Nun, ich weiß, dass C++ einige von ihnen hat, aber sie sind nicht wirklich beliebt, weil sie gezwungen sind, sich mit einer Sprache zu befassen, die überhaupt nicht für die GC-Bearbeitung 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 Sprache ohne GC-Ed hinzuzufügen, ist es auch einfacher, eine neue Sprache zu erstellen, die die gleiche Syntax aufweist und gleichzeitig einen GC unterstützt. Java und C # sind gute Beispiele dafür.

6
hugomg

Können Sie sich vorstellen, einen Gerätehandler in einer Sprache mit Garbage Collection zu schreiben? Wie viele Bits könnten auf der ganzen Linie kommen, während der GC lief?

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

C ist für niedrige Pegel in der Nähe der Hardwareaufgaben ausgelegt. Das Problem? Ist es eine so schöne Sprache, dass es auch für viele übergeordnete Aufgaben eine gute Wahl ist? Die Sprachzaren sind sich dieser Verwendung bewusst, müssen jedoch vorrangig die Anforderungen von Gerätetreibern, eingebettetem Code und Betriebssystemen unterstützen.

6
James Anderson

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 einfach ohne eine zugrunde liegende Nicht-GC-Sprache implementieren.
  • Obwohl GC für typischen Anwendungscode, der in typischen Zeiträumen usw. entwickelt wurde, nachweislich effizienter als Nicht-GC ist, gibt es Probleme, bei denen mehr Entwicklungsaufwand ein guter Kompromiss ist und die spezialisierte Speicherverwaltung eine allgemeine GC übertrifft. Außerdem ist C++ in der Regel nachweislich effizienter als die meisten GC-Sprachen, auch ohne zusätzlichen Entwicklungsaufwand.
  • GC ist nicht allgemein sicherer als RAII im C++ - Stil. Mit RAII können andere Ressourcen als Speicher 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 haben ihre eigenen charakteristischen Arten von Speicherlecks, insbesondere in Bezug auf Speicher, der nie wieder verwendet wird, bei dem jedoch vorhandene Referenzen vorhanden waren, die nie auf Null gesetzt oder überschrieben wurden. Die Notwendigkeit, dies explizit zu tun, unterscheidet sich im Prinzip nicht von der Notwendigkeit, delete oder free explizit zu tun. Der GC-Ansatz hat immer noch einen Vorteil - keine baumelnden Referenzen - und statische Analysen können einige Fälle erfassen, aber auch hier gibt es keine perfekte Lösung für alle Fälle.

Grundsätzlich 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 wie ein Nischenort ist. Und im Ernst, in C++ ist das Fehlen von GC keine große Sache - Ihr Speicher wird anders verwaltet, aber nicht nicht verwaltet.

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

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

5
Steve314

Die Speicherbereinigung ist grundsätzlich nicht kompatibel mit einer Systemsprache, die zur Entwicklung von Treibern für DMA-fähige Hardware verwendet wird.

Es ist durchaus möglich, dass der einzige Zeiger auf ein Objekt in einem Hardwareregister in einem Peripheriegerät gespeichert wird. Da der Garbage Collector nichts davon wissen würde, würde er denken, dass das Objekt nicht erreichbar ist, und es sammeln.

Dieses Argument gilt doppelt für die Komprimierung von GC. Selbst wenn Sie darauf geachtet hätten, speicherinterne Verweise auf Objekte zu pflegen, die von Hardware-Peripheriegeräten verwendet werden, wusste der GC beim Verschieben des Objekts nicht, wie der im Konfigurationsregister für Peripheriegeräte enthaltene Zeiger aktualisiert werden soll.

Jetzt benötigen Sie eine Mischung aus unbeweglichen DMA -Puffern und GC-verwalteten Objekten), was bedeutet, dass Sie alle Nachteile von beiden haben.

4
Ben Voigt

Denn C & C++ sind relativ einfache Sprachen, die für allgemeine Zwecke gedacht sind, beispielsweise um 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.

3
Petruza

Es gibt Garbage Collectors in C++ und C. Sie sind sich nicht sicher, wie dies in C funktioniert, aber in C++ können Sie RTTI nutzen, um Ihr Objektdiagramm dynamisch zu erkennen und zu verwenden für die Müllabfuhr.

Meines Wissens können Sie nicht Java ohne einen Garbage Collector schreiben. Eine kleine Suche ist aufgetaucht this .

Der Hauptunterschied zwischen Java und C/C++) besteht darin, dass Sie in C/C++ immer die Wahl haben, während Sie in Java oft ohne Optionen bleiben) Design.

2
back2dos

Es ist ein Kompromiss zwischen Leistung und Sicherheit.

Es gibt keine Garantie dafür, dass Ihr Müll in Java gesammelt wird, sodass er möglicherweise lange Zeit Speicherplatz beansprucht, während das Scannen nach nicht referenzierten Objekten (dh Müll) auch länger dauert 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 eher korrekten Code erzeugt.

Diese Debatten können manchmal einen leichten „religiösen“ Vorteil haben - seien Sie gewarnt!

2
adrianmcmenamin

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

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

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

  • 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 seine Zeiger gefunden werden können.

    Diese Entdeckung muss perfekt sein: Der GC muss in der Lage sein, alle entdeckten Zeiger 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 stillschweigend beschädigt.

    Dies erfordert unbedingt, dass Typinformationen in jedem einzelnen existierenden Objekt gespeichert werden. Sowohl C als auch C++ ermöglichen jedoch einfache alte Datenobjekte, die keine Typinformationen enthalten.

  • GC ist von Natur aus ein langsames Geschäft. Programmierer, die mit Java] sozialisiert wurden, 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 ist GC. Dies verhindert, dass GCed-Sprachen wie Java) für Supercomputing verwendet werden. Wenn Ihre Maschine einen Stromverbrauch von einer Million pro Jahr kostet, möchten Sie nicht bezahlen sogar 10% davon für die Müllabfuhr.

C und C++ sind Sprachen, die erstellt wurden, um alle möglichen Anwendungsfälle zu unterstützen. Und wie Sie sehen, werden viele dieser Anwendungsfälle durch die Speicherbereinigung ausgeschlossen. Um diese Anwendungsfälle zu unterstützen, kann C/C++ nicht mit Müll gesammelt werden.