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.
garbage-collection
Dark Templar
quelle
quelle
Antworten:
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 ++.
quelle
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.
quelle
malloc()
oder nicht anfree()
. (Beispiel sind MLAP-Compiler für PIC)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
using
Schlü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. (zBFoo&
).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.
quelle
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ürdestd:unique_ptr
einen 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 (undstd::move
der Mechanismus zur Aktualisierung der Referenzzählung).std::unique_ptr
hat keinen Referenzzähler undstd::move
hat überhaupt nichts mit Referenzen zu tun (also "kein" Leistungseinbruch). Ich verstehe Ihren Standpunkt jedoch, dastd::shared_ptr
es eine Referenzzählung gibt, die implizit vonstd::move
:) aktualisiert wirdmalloc
und seinfree
. 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.)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.
quelle
"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.
quelle
malloc
und anschließenfree
, würden Sie mein korrektes Programm unterbrechen.free
wenn ich damit fertig wäre. Aber Ihr vorgeschlagener Garbage Collector, der den Speicher erst freigibt, wenn ich ihn explizit aufrufe,free
ist überhaupt kein Garbage Collector.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.
quelle
Ich habe nicht die genauen Zitate, aber Bjarne und Herb Sutter sagen etwas in die richtige Richtung:
In modernem C ++ verwenden Sie intelligente Zeiger und haben daher keinen Müll.
quelle
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.
quelle
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.
quelle
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.
quelle
Es gibt verschiedene Probleme, darunter ...
delete
oderfree
explizit. 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 ...
quelle
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.
quelle
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.
quelle
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.
quelle
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!
quelle
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.
quelle